/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 map_renderer.cpp

	$Header: /heroes4/map_renderer.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "map_renderer.h"

#include <algorithm>
#include <assert.h>
#include <map>
#include <string.h>
#include <utility>

#include "abstract_adv_object.h"
#include "abstract_adventure_map.h"
#include "alpha_blend.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_group_ptr.h"
#include "bitmap_layer.h"
#include "clip_list.h"
#include "convert_16_bit.h"
#include "draw_terrain.h"
#include "enumerate_tile_points_within_screen_rect.h"
#include "footprint.h"
#include "game_time.h"
#include "idle_processor.h"
#include "map_point.h"
#include "memory_bitmap.h"
#include "metatile_map.h"
#include "pixel_24.h"
#include "player_color.h"
#include "quad.h"
#include "screen_point.h"
#include "sized_int_types.h"
#include "shroud_transition_map.h"
#include "sorted_clip_list.h"
#include "transitions.h"
#include "vector_set.h"


using namespace map_renderer_details;

/////////////////////////////////////////////////////////////////////////////
// Unnamed namespace

namespace
{

	/////////////////////////////////////////////////////////////////////////////
	// t_adventure_metatile_map class

	class t_adventure_metatile_map : public t_metatile_map
	{
	public:
		// Types
		typedef t_abstract_adventure_map t_adventure_map;

		// Constructor
		explicit t_adventure_metatile_map( t_adventure_map const & adventure_map );

	private:
		// Types
		class t_adventure_map_adapter;
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_adventure_metatile_map::t_adventure_map_adapter class

	class t_adventure_metatile_map::t_adventure_map_adapter
		:	public t_metatile_map::t_abstract_tile_map
	{
	public:
		// Constructor
		explicit t_adventure_map_adapter( t_adventure_map const & adventure_map );

		// Member functions
		virtual void get_size( int & size, int & num_levels ) const;
		virtual void get_row_bounds( int row, int & start, int & end ) const;

	private:
		// Data members
		t_adventure_map const &	m_adventure_map;
	};

	inline t_adventure_metatile_map::t_adventure_map_adapter::t_adventure_map_adapter(
		t_adventure_map const & adventure_map )
		:	m_adventure_map( adventure_map )
	{
	}

	void t_adventure_metatile_map::t_adventure_map_adapter::get_size(
		int &	size,
		int &	num_levels ) const
	{
		size = m_adventure_map.get_size();
		num_levels = m_adventure_map.get_num_levels();
	}

	void t_adventure_metatile_map::t_adventure_map_adapter::get_row_bounds(
		int		row,
		int &	start,
		int &	end ) const
	{
		start = m_adventure_map.get_row_start( row );
		end = m_adventure_map.get_row_end( row );
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_adventure_metatile_map members

	inline t_adventure_metatile_map::t_adventure_metatile_map(
		t_adventure_map const & adventure_map )
		:	t_metatile_map(
				t_adventure_map_adapter( adventure_map ),
				adventure_map.get_terrain_random_seed() )
	{
	}

	/////////////////////////////////////////////////////////////////////////////
	int const k_margin_column_width = k_adventure_tile_width / 2;

	/////////////////////////////////////////////////////////////////////////////
	enum t_map_margin_type
	{
		k_map_margin_none,
		k_map_margin_top,
		k_map_margin_bottom,

		k_map_margin_type_count,
	};

	/////////////////////////////////////////////////////////////////////////////
	t_map_margin_type compute_tile_point_margin_type(
		t_abstract_adventure_map const &	adventure_map,
		t_map_point_2d const &				tile_point )
	{
		int map_size = adventure_map.get_size();
		int half_map_size = map_size / 2;

		int coord_sum = tile_point.row + tile_point.column;
		if ( coord_sum == half_map_size - 1 )
			return k_map_margin_top;
		if ( coord_sum == map_size + half_map_size - 1 )
			return k_map_margin_bottom;

		return k_map_margin_none;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline bool is_top_margin_tile_point(
		t_abstract_adventure_map const &	adventure_map,
		t_map_point_2d const &				tile_point )
	{
		return compute_tile_point_margin_type( adventure_map, tile_point ) == k_map_margin_top;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline bool is_bottom_margin_tile_point(
		t_abstract_adventure_map const &	adventure_map,
		t_map_point_2d const &				tile_point )
	{
		return compute_tile_point_margin_type( adventure_map, tile_point ) == k_map_margin_bottom;
	}

	/////////////////////////////////////////////////////////////////////////////
	t_map_point_2d compute_top_margin_column_tile_point(
		t_abstract_adventure_map const &	adventure_map,
		int									column_num )
	{
		int coord_sum = adventure_map.get_size() / 2 - 1;
		int offset = column_num >> 1;
		return t_map_point_2d( coord_sum - offset, offset );
	}

	/////////////////////////////////////////////////////////////////////////////
	t_map_point_2d compute_bottom_margin_column_tile_point(
		t_abstract_adventure_map const &	adventure_map,
		int									column_num )
	{
		int half_map_size = adventure_map.get_size() / 2;
		return		compute_top_margin_column_tile_point( adventure_map, column_num )
				+	t_map_point_2d( half_map_size, half_map_size );
	}

	/////////////////////////////////////////////////////////////////////////////
	inline int compute_margin_column_num(
		t_abstract_adventure_map const &	adventure_map,
		t_map_point_2d const &				tile_point )
	{
		return tile_point.column - tile_point.row + adventure_map.get_size() / 2 - 1;
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_shroud_bitmap class

	class t_shroud_bitmap : public t_memory_bitmap16, private t_pixel_mask_viewer
	{
	public:
		// Constructor
		t_shroud_bitmap();

	private:
		// Member functions
		void			init();
		virtual void	on_pixel_masks_changed();
	};

	/////////////////////////////////////////////////////////////////////////////
	// shroud bitmap implementation

	/////////////////////////////////////////////////////////////////////////////
	t_shroud_bitmap::t_shroud_bitmap()
	{
		init();
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_shroud_bitmap::init()
	{
		static t_bitmap_group_cache texture_cache( "shroud" );

		t_bitmap_group_ptr texture_ptr = texture_cache.get();

		// use draw_to to copy the bitmap layer data to local shroud data
		assert( texture_ptr->front().get() != 0 );
		t_bitmap_layer const & layer = *texture_ptr->front();

		t_memory_bitmap16::init( layer.get_width(), layer.get_height() );

		layer.draw_to( *this );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_shroud_bitmap::on_pixel_masks_changed()
	{
		init();
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_shroud_bitmap const & get_shroud_texture()
	{
		static t_shroud_bitmap instance;
		return instance;
	}

	/////////////////////////////////////////////////////////////////////////////
	// Non-member functions

	/////////////////////////////////////////////////////////////////////////////
	t_bitmap_layer const & find(
		t_bitmap_group const &	group,
		char const *			name )
	{
		t_bitmap_layer const * result_ptr = group.find( name );
		assert( result_ptr != 0 );
		return *result_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_border_frame_layers class

	class t_border_frame_layers
	{
	public:
		// Constructor
		t_border_frame_layers();

		// Member functions
		t_bitmap_layer const &	get_bottom() const;
		t_bitmap_layer const &	get_bottom_left() const;
		t_bitmap_layer const &	get_bottom_right() const;
		t_bitmap_layer const &	get_left() const;
		t_bitmap_layer const &	get_right() const;
		t_bitmap_layer const &	get_top() const;
		t_bitmap_layer const &	get_top_left() const;
		t_bitmap_layer const &	get_top_right() const;

	private:
		// Data members
		t_bitmap_group_ptr		m_group_ptr;
		t_bitmap_layer const *	m_bottom_ptr;
		t_bitmap_layer const *	m_corner_ptr;
		t_bitmap_layer const *	m_left_ptr;
		t_bitmap_layer const *	m_right_ptr;
		t_bitmap_layer const *	m_top_ptr;
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_border_frame_layers members

	/////////////////////////////////////////////////////////////////////////////
	t_border_frame_layers::t_border_frame_layers()
	{
		static t_bitmap_group_cache cache( "map_frame" );
		m_group_ptr = cache.get();
		m_bottom_ptr = &find( *m_group_ptr, "bottom" );
		m_corner_ptr = &find( *m_group_ptr, "corner" );
		m_left_ptr = &find( *m_group_ptr, "left" );
		m_right_ptr = &find( *m_group_ptr, "right" );
		m_top_ptr = &find( *m_group_ptr, "top" );
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_bottom() const
	{
		return *m_bottom_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_bottom_left() const
	{
		return *m_corner_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_bottom_right() const
	{
		return *m_corner_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_left() const
	{
		return *m_left_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_right() const
	{
		return *m_right_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_top() const
	{
		return *m_top_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_top_left() const
	{
		return *m_corner_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_border_frame_layers::get_top_right() const
	{
		return *m_corner_ptr;
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_margin_texture_layers class

	class t_margin_texture_layers
	{
	public:
		// Constructor
		t_margin_texture_layers();

		// Member functions
		t_bitmap_layer const &	get_bottom() const;
		t_bitmap_layer const &	get_top() const;

	private:
		// Data members
		t_bitmap_group_ptr		m_group_ptr;
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_margin_texture_layers members

	/////////////////////////////////////////////////////////////////////////////
	t_margin_texture_layers::t_margin_texture_layers()
	{
		static t_bitmap_group_cache cache( "shroud" );
		m_group_ptr = cache.get();
		assert( !m_group_ptr->empty() );
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_margin_texture_layers::get_bottom() const
	{
		return *m_group_ptr->front();
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer const & t_margin_texture_layers::get_top() const
	{
		return *m_group_ptr->front();
	}

} // Unnamed namespace

/////////////////////////////////////////////////////////////////////////////
// Details namespace

namespace map_renderer_details
{
	/////////////////////////////////////////////////////////////////////////////
	// t_internal_map_data class

	class t_internal_map_data : public t_counted_object
	{
	public:
		// Types
		typedef t_map_renderer::t_adv_map					t_adventure_map;
		typedef t_map_renderer::t_object					t_object;
		typedef t_map_renderer::t_object_id					t_object_id;
		typedef t_map_renderer::t_object_id_set				t_object_id_set;
		typedef t_map_renderer::t_tile_point_set			t_tile_point_set;
		typedef t_map_renderer::t_object_height_delta_set	t_object_height_delta_set;

		// Constructor
		t_internal_map_data( t_adventure_map const & adventure_map );

		// Member functions
		int						add_map_renderer( t_map_renderer * map_renderer_ptr );
		t_adventure_map const &	get_adventure_map() const;
		int						get_bottom_margin_column_height( int level, int margin_column_num ) const;
		int						get_bottom_margin_height( int level, int x ) const;
		int						get_bottom_margin_point_height( int level, int margin_point_num ) const;
		t_metatile_map const &	get_metatile_map() const;
		int						get_top_margin_column_height( int level, int margin_column_num ) const;
		int						get_top_margin_height( int level, int x ) const;
		int						get_top_margin_point_height( int level, int margin_point_num ) const;
		void					on_adv_object_moved( t_object_id object_id );
		void					on_adv_object_moved(
									t_object_id							object_id,
									int									level,
									t_tile_point_set const &			changed_tile_points,
									t_object_height_delta_set const &	changed_object_height_deltas );
		void					on_adv_object_placed( t_object_id object_id );
		void					on_adv_object_placed(
									t_object_id							object_id,
									int									level,
									t_tile_point_set const &			changed_tile_points,
									t_object_height_delta_set const &	changed_object_height_deltas );
		void					on_adv_object_removed(
									t_object const &				object,
									t_level_map_point_2d const &	object_pos );
		void					on_moving_adv_object( t_object_id object_id );
		void					on_multiple_adv_objects_removed();
		void					on_removing_adv_object( t_object_id object_id );
		void					on_removing_multiple_adv_objects(
									t_object_id_set const &	object_ids );
		void					on_rock_terrain_changed(
									int							level,
									t_tile_point_set const &	changed_tile_points,
									t_object_id_set const &		placed_object_ids );
		void					on_terrain_changed(
									int							level,
									t_tile_point_set const &	changed_tile_points );
		void					on_terrain_changed(
									int									level,
									t_tile_point_set const &			changed_tile_points,
									t_object_height_delta_set const &	changed_object_height_deltas );
		void					on_visibility_changed(
									int							team_num,
									int							level,
									t_visibility_set const &	changed_visibility_set,
									t_tile_point_set const &	changed_shroud_set );
		void					refresh();
		int						remove_map_renderer( t_map_renderer * map_renderer_ptr );

	private:
		// Types
		typedef t_vector_set< t_map_renderer * > t_map_renderer_ptr_set;

		struct t_map_margin_point_heights
		{
			int	top;
			int	bottom;
		};
		typedef std::vector< t_map_margin_point_heights >			t_map_margin_point_heights_vector;
		typedef std::vector< t_map_margin_point_heights_vector >	t_map_margin_point_heights_vector_vector;

		// Data members
		t_adventure_map const &						m_adventure_map;
		t_map_renderer_ptr_set						m_map_renderer_ptrs;

		t_adventure_metatile_map					m_metatile_map;
		t_map_margin_point_heights_vector_vector	m_map_margin_point_heights_vector_vector;

		// Member functions
		void	compute_margin_point_heights();
		void	on_terrain_changed_helper(
					int							level,
					t_tile_point_set const &	changed_tile_points,
					std::map< int, int > &		top_margin_column_height_deltas,
					std::map< int, int > &		bottom_margin_column_height_deltas );
	};

	t_internal_map_data::t_internal_map_data( t_adventure_map const & adventure_map )
		:	m_adventure_map( adventure_map ),
			m_metatile_map( adventure_map )
	{
		int map_size = m_adventure_map.get_size();
		int num_levels = m_adventure_map.get_num_levels();

		m_map_margin_point_heights_vector_vector.resize( num_levels );
		
		int level;
		for ( level = 0; level < num_levels; ++level )
		{
			t_map_margin_point_heights_vector & map_margin_point_heights_vector =
				m_map_margin_point_heights_vector_vector[ level ];
			map_margin_point_heights_vector.resize( map_size + 1 );
		}

		compute_margin_point_heights();
	}

	int t_internal_map_data::add_map_renderer( t_map_renderer * map_renderer_ptr )
	{
		assert( map_renderer_ptr != 0 );
		assert( m_map_renderer_ptrs.find( map_renderer_ptr ) == m_map_renderer_ptrs.end() );
		m_map_renderer_ptrs.insert( map_renderer_ptr );
		return m_map_renderer_ptrs.size();
	}
	
	inline t_abstract_adventure_map const & t_internal_map_data::get_adventure_map() const
	{
		return m_adventure_map;
	}

	inline int t_internal_map_data::get_bottom_margin_column_height( int level, int margin_column_num ) const
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );
		assert( margin_column_num >= 0 && margin_column_num < m_adventure_map.get_size() );
		t_map_margin_point_heights_vector const & margin_point_heights_vector = m_map_margin_point_heights_vector_vector[ level ];
		int left_height = margin_point_heights_vector[ margin_column_num ].bottom;
		int right_height = margin_point_heights_vector[ margin_column_num + 1 ].bottom;
		return left_height < right_height ? left_height : right_height;
	}

	int t_internal_map_data::get_bottom_margin_height( int level, int x ) const
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );
		assert( x >= 0 && x < m_adventure_map.get_size() * k_margin_column_width );
		int column_num = x / k_margin_column_width;
		int column_x = x % k_margin_column_width;
		t_map_margin_point_heights_vector const & margin_point_heights_vector = m_map_margin_point_heights_vector_vector[ level ];
		int left_height = margin_point_heights_vector[ column_num ].bottom;
		int right_height = margin_point_heights_vector[ column_num + 1 ].bottom;

		int result;
		if ( left_height >= right_height )
			result = left_height + ( right_height - left_height ) * column_x / k_margin_column_width;
		else
			result = right_height + ( left_height - right_height ) * ( k_margin_column_width - column_x - 1 ) / k_margin_column_width;
		return result;
	}

	inline int t_internal_map_data::get_bottom_margin_point_height( int level, int margin_point_num ) const
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );
		assert( margin_point_num >= 0 && margin_point_num <= m_adventure_map.get_size() );
		return m_map_margin_point_heights_vector_vector[ level ][ margin_point_num ].bottom;
	}

	inline t_metatile_map const & t_internal_map_data::get_metatile_map() const
	{
		return m_metatile_map;
	}

	inline int t_internal_map_data::get_top_margin_column_height( int level, int margin_column_num ) const
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );
		assert( margin_column_num >= 0 && margin_column_num < m_adventure_map.get_size() );
		t_map_margin_point_heights_vector const & margin_point_heights_vector = m_map_margin_point_heights_vector_vector[ level ];
		int left_height = margin_point_heights_vector[ margin_column_num ].top;
		int right_height = margin_point_heights_vector[ margin_column_num + 1 ].top;
		return left_height > right_height ? left_height : right_height;
	}

	int t_internal_map_data::get_top_margin_height( int level, int x ) const
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );
		assert( x >= 0 && x < m_adventure_map.get_size() * k_margin_column_width );
		int column_num = x / k_margin_column_width;
		int column_x = x % k_margin_column_width;
		t_map_margin_point_heights_vector const & margin_point_heights_vector = m_map_margin_point_heights_vector_vector[ level ];
		int left_height = margin_point_heights_vector[ column_num ].top;
		int right_height = margin_point_heights_vector[ column_num + 1 ].top;

		int result;
		if ( left_height <= right_height )
			result = left_height + ( right_height - left_height ) * column_x / k_margin_column_width;
		else
			result = right_height + ( left_height - right_height ) * ( k_margin_column_width - column_x - 1 ) / k_margin_column_width;
		return result;
	}

	inline int t_internal_map_data::get_top_margin_point_height( int level, int margin_point_num ) const
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );
		assert( margin_point_num >= 0 && margin_point_num <= m_adventure_map.get_size() );
		return m_map_margin_point_heights_vector_vector[ level ][ margin_point_num ].top;
	}

	int t_internal_map_data::remove_map_renderer( t_map_renderer * map_renderer_ptr )
	{
		assert( map_renderer_ptr != 0 );
		assert( m_map_renderer_ptrs.find( map_renderer_ptr ) != m_map_renderer_ptrs.end() );
		m_map_renderer_ptrs.erase( map_renderer_ptr );
		return m_map_renderer_ptrs.size();
	}

	void t_internal_map_data::compute_margin_point_heights()
	{
		int map_size = m_adventure_map.get_size();
		int half_map_size = map_size / 2;
		int num_levels = m_adventure_map.get_num_levels();

		int level;
		for ( level = 0; level < num_levels; ++level )
		{
			t_map_margin_point_heights_vector & margin_point_heights_vector
				= m_map_margin_point_heights_vector_vector[ level ];

			// Do the even margin points first
			t_map_point_2d top_margin_vertex_point( half_map_size, 0 );
			int margin_point_num;
			for ( margin_point_num = 0; margin_point_num <= map_size; margin_point_num += 2 )
			{
				t_map_point_2d bottom_margin_vertex_point = top_margin_vertex_point + t_map_point_2d( half_map_size, half_map_size );

				t_map_margin_point_heights & margin_point_heights = margin_point_heights_vector[ margin_point_num ];
				margin_point_heights.top = m_adventure_map.get_tile_vertex( t_level_map_point_2d( top_margin_vertex_point, level ) ).get_height();
				margin_point_heights.bottom = m_adventure_map.get_tile_vertex( t_level_map_point_2d( bottom_margin_vertex_point, level ) ).get_height();

				top_margin_vertex_point += t_map_point_2d( -1, 1 );
			}

			// Do the odd margin points next
			top_margin_vertex_point = t_map_point_2d( half_map_size - 1, 0 );
			for ( margin_point_num = 1; margin_point_num < map_size; margin_point_num += 2 )
			{
				t_map_point_2d bottom_margin_vertex_point = top_margin_vertex_point + t_map_point_2d( half_map_size, half_map_size );

				t_map_margin_point_heights & margin_point_heights = margin_point_heights_vector[ margin_point_num ];

				int top_height = m_adventure_map.get_tile_vertex( t_level_map_point_2d( top_margin_vertex_point, level ) ).get_height();
				int bottom_height = m_adventure_map.get_tile_vertex( t_level_map_point_2d( top_margin_vertex_point + t_map_point_2d( 1, 1 ), level ) ).get_height();
				margin_point_heights.top = ( top_height + bottom_height ) / 2;

				top_height = m_adventure_map.get_tile_vertex( t_level_map_point_2d( bottom_margin_vertex_point, level ) ).get_height();
				bottom_height = m_adventure_map.get_tile_vertex( t_level_map_point_2d( bottom_margin_vertex_point + t_map_point_2d( 1, 1 ), level ) ).get_height();
				margin_point_heights.bottom = ( top_height + bottom_height ) / 2;

				top_margin_vertex_point += t_map_point_2d( -1, 1 );
			}
		}
	}

	void t_internal_map_data::on_terrain_changed_helper(
		int							level,
		t_tile_point_set const &	changed_tile_points,
		std::map< int, int > &		top_margin_column_height_deltas,
		std::map< int, int > &		bottom_margin_column_height_deltas )
	{
		assert( level >= 0 && level < m_adventure_map.get_num_levels() );

		int map_size = m_adventure_map.get_size();
		int half_map_size = map_size / 2;

		std::map< int, int > top_margin_point_height_change_log;
		std::map< int, int > bottom_margin_point_height_change_log;
		std::map< int, int > new_top_margin_column_height_deltas;
		std::map< int, int > new_bottom_margin_column_height_deltas;

		t_map_margin_point_heights_vector & margin_point_heights_vector = m_map_margin_point_heights_vector_vector[ level ];

		// Run through all of the changed tile points looking for changes to the margin tiles
		t_tile_point_set::const_iterator changed_tile_point_iter = changed_tile_points.begin();
		for ( ; changed_tile_point_iter != changed_tile_points.end(); ++changed_tile_point_iter )
		{
			t_map_point_2d tile_point = *changed_tile_point_iter;
			t_map_margin_type margin_type = compute_tile_point_margin_type( m_adventure_map, tile_point );
			if ( margin_type == k_map_margin_none )
				continue;

			// Get the tile vertex quad for this tile
			t_adventure_map::t_const_tile_vertex_quad tile_vertex_quad =
				m_adventure_map.get_tile_vertex_quad( t_level_map_point_2d( tile_point, level ) );

			int left_margin_point_height = tile_vertex_quad.left.get_height();
			int middle_margin_point_height = ( tile_vertex_quad.top.get_height() + tile_vertex_quad.bottom.get_height() ) / 2;
			int right_margin_point_height = tile_vertex_quad.right.get_height();

			int middle_margin_point_num = half_map_size - tile_point.row + tile_point.column;
			assert( middle_margin_point_num >= 0 && middle_margin_point_num <= map_size );
			assert( ( middle_margin_point_num & 1 ) != 0 );
			int left_margin_point_num = middle_margin_point_num - 1;
			int right_margin_point_num = middle_margin_point_num + 1;

			if ( margin_type == k_map_margin_top )
			{
				int old_left_margin_point_height = margin_point_heights_vector[ left_margin_point_num ].top;
				int old_middle_margin_point_height = margin_point_heights_vector[ middle_margin_point_num ].top;
				int old_right_margin_point_height = margin_point_heights_vector[ right_margin_point_num ].top;

				// Log any margin point height movement
				if ( left_margin_point_height != old_left_margin_point_height )
					top_margin_point_height_change_log.insert( std::make_pair( left_margin_point_num, left_margin_point_height ) );
				if ( middle_margin_point_height != old_middle_margin_point_height )
					top_margin_point_height_change_log.insert( std::make_pair( middle_margin_point_num, middle_margin_point_height ) );
				if ( right_margin_point_height != old_right_margin_point_height )
					top_margin_point_height_change_log.insert( std::make_pair( right_margin_point_num, right_margin_point_height ) );

				// Compute the old and new column heights
				int new_left_margin_column_height = left_margin_point_height > middle_margin_point_height ? left_margin_point_height : middle_margin_point_height;
				int new_right_margin_column_height = middle_margin_point_height > right_margin_point_height ? middle_margin_point_height : right_margin_point_height;
				int old_left_margin_column_height = old_left_margin_point_height > old_middle_margin_point_height ? old_left_margin_point_height : old_middle_margin_point_height;
				int old_right_margin_column_height = old_middle_margin_point_height > old_right_margin_point_height ? old_middle_margin_point_height : old_right_margin_point_height;

				// Log any downward height movement
				if ( new_left_margin_column_height < old_left_margin_column_height )
					new_top_margin_column_height_deltas.insert( std::make_pair( left_margin_point_num, new_left_margin_column_height - old_left_margin_column_height ) );
				if ( new_right_margin_column_height < old_right_margin_column_height )
					new_top_margin_column_height_deltas.insert( std::make_pair( middle_margin_point_num, new_right_margin_column_height - old_right_margin_column_height ) );
			}
			else
			{
				assert( margin_type == k_map_margin_bottom );

				int old_left_margin_point_height = margin_point_heights_vector[ left_margin_point_num ].bottom;
				int old_middle_margin_point_height = margin_point_heights_vector[ middle_margin_point_num ].bottom;
				int old_right_margin_point_height = margin_point_heights_vector[ right_margin_point_num ].bottom;

				// Log any margin point height movement
				if ( left_margin_point_height != old_left_margin_point_height )
					bottom_margin_point_height_change_log.insert( std::make_pair( left_margin_point_num, left_margin_point_height ) );
				if ( middle_margin_point_height != old_middle_margin_point_height )
					bottom_margin_point_height_change_log.insert( std::make_pair( middle_margin_point_num, middle_margin_point_height ) );
				if ( right_margin_point_height != old_right_margin_point_height )
					bottom_margin_point_height_change_log.insert( std::make_pair( right_margin_point_num, right_margin_point_height ) );

				// Compute the old and new column heights
				int new_left_margin_column_height = left_margin_point_height < middle_margin_point_height ? left_margin_point_height : middle_margin_point_height;
				int new_right_margin_column_height = middle_margin_point_height < right_margin_point_height ? middle_margin_point_height : right_margin_point_height;
				int old_left_margin_column_height = old_left_margin_point_height < old_middle_margin_point_height ? old_left_margin_point_height : old_middle_margin_point_height;
				int old_right_margin_column_height = old_middle_margin_point_height < old_right_margin_point_height ? old_middle_margin_point_height : old_right_margin_point_height;

				// Log any upward height movement
				if ( new_left_margin_column_height > old_left_margin_column_height )
					new_bottom_margin_column_height_deltas.insert( std::make_pair( left_margin_point_num, new_left_margin_column_height - old_left_margin_column_height ) );
				if ( new_right_margin_column_height > old_right_margin_column_height )
					new_bottom_margin_column_height_deltas.insert( std::make_pair( middle_margin_point_num, new_right_margin_column_height - old_right_margin_column_height ) );
			}
		}

		// Now realize all of the margin point height changes
		std::map< int, int >::const_iterator margin_point_height_change_iter = top_margin_point_height_change_log.begin();
		for (	;
				margin_point_height_change_iter != top_margin_point_height_change_log.end();
				++margin_point_height_change_iter )
		{
			int margin_point_num = margin_point_height_change_iter->first;
			assert( margin_point_num >= 0 && margin_point_num <= map_size );
			int new_height = margin_point_height_change_iter->second;
			margin_point_heights_vector[ margin_point_num ].top = new_height;
		}

		for (	margin_point_height_change_iter = bottom_margin_point_height_change_log.begin();
				margin_point_height_change_iter != bottom_margin_point_height_change_log.end();
				++margin_point_height_change_iter )
		{
			int margin_point_num = margin_point_height_change_iter->first;
			assert( margin_point_num >= 0 && margin_point_num <= map_size );
			int new_height = margin_point_height_change_iter->second;
			margin_point_heights_vector[ margin_point_num ].bottom = new_height;
		}

		// Set the return values
		new_top_margin_column_height_deltas.swap( top_margin_column_height_deltas );
		new_bottom_margin_column_height_deltas.swap( bottom_margin_column_height_deltas );
	}

} // Details namespace

/////////////////////////////////////////////////////////////////////////////
// Unnamed namespace
typedef t_counted_ptr<t_internal_map_data> t_internal_data_ptr;

namespace
{

	/////////////////////////////////////////////////////////////////////////////
	// t_internal_map_data_map typedef

	class t_internal_map_data_map : public t_counted_object, 
		public std::map< t_abstract_adventure_map const *, t_internal_data_ptr >
	{
	};

	typedef t_counted_ptr<t_internal_map_data_map> t_internal_map_list_ptr;

	/////////////////////////////////////////////////////////////////////////////
	// Accessor function for the single t_internal_map_data_map instance

	t_internal_map_list_ptr get_internal_map_list()
	{
		static t_internal_map_list_ptr instance = new t_internal_map_data_map;

		return instance;
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_internal_map_data * get_internal_map_data_ptr(
		t_abstract_adventure_map const & adventure_map )
	{
		t_internal_map_list_ptr map_list = get_internal_map_list();

		t_internal_map_data_map::const_iterator internal_map_data_iter 
			= map_list->find( &adventure_map );
		if ( internal_map_data_iter == map_list->end() )
			return 0;
		return internal_map_data_iter->second.get();
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_bitmap_1d class template

	template < typename t_pixel_arg >
	class t_bitmap_1d : private t_uncopyable
	{
	public:
		// Types
		typedef t_pixel_arg	t_pixel;

		// Constructor
		t_bitmap_1d();
		t_bitmap_1d( int size );

		// Member functions
		t_pixel *		get_data_ptr();
		t_pixel const *	get_data_ptr() const;
		int				get_size() const;
		void			init( int size );

	private:
		// Data members
		t_owned_array< t_pixel >	m_data_ptr;
		int							m_size;
	};

	template < typename t_pixel_arg >
	inline t_bitmap_1d< t_pixel_arg >::t_bitmap_1d()
		:	m_size( 0 )
	{
	}

	template < typename t_pixel_arg >
	inline t_bitmap_1d< t_pixel_arg >::t_bitmap_1d( int size )
	{
		init( size );
	}

	template < typename t_pixel_arg >
	inline typename t_bitmap_1d< t_pixel_arg >::t_pixel *
	t_bitmap_1d< t_pixel_arg >::get_data_ptr()
	{
		return m_data_ptr.get();
	}

	template < typename t_pixel_arg >
	inline typename t_bitmap_1d< t_pixel_arg >::t_pixel const *
	t_bitmap_1d< t_pixel_arg >::get_data_ptr() const
	{
		return m_data_ptr.get();
	}

	template < typename t_pixel_arg >
	inline int t_bitmap_1d< t_pixel_arg >::get_size() const
	{
		return m_size;
	}

	template < typename t_pixel_arg >
	void t_bitmap_1d< t_pixel_arg >::init( int size )
	{
		assert( size >= 0 );
		m_data_ptr.reset( new t_pixel [ size ] );
		m_size = size;
	}

	/////////////////////////////////////////////////////////////////////////////
	void clip_to_dest(
		t_screen_point const &	dest_size,
		t_screen_point &		dest_point,
		t_screen_point &		src_point,
		t_screen_point &		blt_size )
	{
		// Clip to destination left
		if ( dest_point.x < 0 )
		{
			blt_size.x += dest_point.x;
			src_point.x -= dest_point.x;
			dest_point.x = 0;
		}

		// Clip to destination right
		if ( dest_point.x + blt_size.x > dest_size.x )
			blt_size.x = dest_size.x - dest_point.x;

		// Clip to destination top
		if ( dest_point.y < 0 )
		{
			blt_size.y += dest_point.y;
			src_point.y -= dest_point.y;
			dest_point.y = 0;
		}

		// Clip to destination bottom
		if ( dest_point.y + blt_size.y > dest_size.y )
			blt_size.y = dest_size.y - dest_point.y;
	}

	/////////////////////////////////////////////////////////////////////////////
	void clip(
		t_screen_point const &	dest_size,
		t_screen_point const &	src_size,
		t_screen_point &		dest_point,
		t_screen_point &		src_point,
		t_screen_point &		blt_size )
	{
		clip_to_dest( dest_size, dest_point, src_point, blt_size );

		// Clip to source left
		if ( src_point.x < 0 )
		{
			blt_size.x += src_point.x;
			dest_point.x -= src_point.x;
			src_point.x = 0;
		}

		// Clip to source right
		if ( src_point.x + blt_size.x > src_size.x )
			blt_size.x = src_size.x - src_point.x;

		// Clip to source top
		if ( src_point.y < 0 )
		{
			blt_size.y += src_point.y;
			dest_point.y -= src_point.y;
			src_point.y = 0;
		}

		// Clip to source bottom
		if ( src_point.y + blt_size.y > src_size.y )
			blt_size.y = src_size.y - src_point.y;
	}

	/////////////////////////////////////////////////////////////////////////////
	void clip_horizontal_source(
		t_screen_point const &	dest_size,
		int						src_size,
		t_screen_point &		dest_point,
		int &					src_point,
		t_screen_point &		blt_size )
	{
		// Clip to destination left
		if ( dest_point.x < 0 )
		{
			blt_size.x += dest_point.x;
			src_point -= dest_point.x;
			dest_point.x = 0;
		}

		// Clip to destination right
		if ( dest_point.x + blt_size.x > dest_size.x )
			blt_size.x = dest_size.x - dest_point.x;

		// Clip to destination top
		if ( dest_point.y < 0 )
		{
			blt_size.y += dest_point.y;
			dest_point.y = 0;
		}

		// Clip to destination bottom
		if ( dest_point.y + blt_size.y > dest_size.y )
			blt_size.y = dest_size.y - dest_point.y;

		// Clip to source left
		if ( src_point < 0 )
		{
			blt_size.x += src_point;
			dest_point.x -= src_point;
			src_point = 0;
		}

		// Clip to source right
		if ( src_point + blt_size.x > src_size )
			blt_size.x = src_size - src_point;
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void bit_blt(
		t_abstract_bitmap< t_pixel > &			dest,
		t_screen_point 							dest_point,
		t_abstract_bitmap< t_pixel > const &	src,
		t_screen_point 							src_point,
		t_screen_point							blt_size )
	{
		assert( &src != &dest );

		clip(
			t_screen_point( dest.get_width(), dest.get_height() ),
			t_screen_point( src.get_width(), src.get_height() ),
			dest_point, src_point, blt_size );

		if ( blt_size.x <= 0 || blt_size.y <= 0 )
			return;

		size_t const line_copy_size = blt_size.x * sizeof( t_pixel );

		t_pixel * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_point.y ) + dest_point.x;
		t_pixel const * src_ptr = src.advance_line( src.get_data_ptr(), src_point.y ) + src_point.x;

		int y;
		for ( y = 0; y < blt_size.y; ++y )
		{
			memcpy( dest_ptr, src_ptr, line_copy_size );

			dest_ptr = dest.advance_line( dest_ptr );
			src_ptr = src.advance_line( src_ptr );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void bit_blt_horizontal_source(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_point					dest_point,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_point					blt_size )
	{
		clip_horizontal_source(
			t_screen_point( dest.get_width(), dest.get_height() ),
			src.get_size(),
			dest_point,
			src_point,
			blt_size );

		if ( blt_size.x <= 0 || blt_size.y <= 0 )
			return;

		size_t const line_copy_size = blt_size.x * sizeof( t_pixel );

		t_pixel * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_point.y ) + dest_point.x;
		t_pixel const * src_ptr = src.get_data_ptr() + src_point;

		int y;
		for ( y = 0; y < blt_size.y; ++y )
		{
			memcpy( dest_ptr, src_ptr, line_copy_size );
			dest_ptr = dest.advance_line( dest_ptr );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void bit_blt_shroud_with_mask(
		t_abstract_bitmap16 &		dest,
		t_screen_point 				dest_point,
		t_abstract_bitmap16 const &	src,
		t_screen_point 				src_point,
		t_abstract_bitmap8 const &	shroud_mask,
		t_screen_point 				mask_point,
		t_screen_point				blt_size )
	{
		assert( &src != &dest );

		clip_to_dest(
			t_screen_point( dest.get_width(), dest.get_height() ),
			dest_point, src_point, blt_size );

		int src_width = src.get_width();
		int src_height = src.get_height();

		// wrap src x and y to tex size
		if ( src_point.x >= 0 )
			src_point.x %= src_width;
		else
			src_point.x = src_width - ( ( -src_point.x - 1 ) % src_width + 1 );
		if ( src_point.y >= 0 )
			src_point.y %= src_height;
		else
			src_point.y = src_height - ( ( -src_point.y - 1 ) % src_height + 1 );

		if ( blt_size.x <= 0 || blt_size.y <= 0 )
			return;

		t_uint16 * line_dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_point.y ) + dest_point.x;
		t_uint16 const * line_src_ptr = src.advance_line( src.get_data_ptr(), src_point.y ) + src_point.x;
		t_uint8 const * line_mask_ptr = shroud_mask.advance_line( shroud_mask.get_data_ptr(), mask_point.y ) + mask_point.x;

		int y;
		int src_y = src_point.y;
		for ( y = 0; y < blt_size.y; ++y )
		{
			int x;

			t_uint16 * dest_ptr = line_dest_ptr;
			t_uint16 const * src_ptr = line_src_ptr;
			t_uint8 const * mask_ptr = line_mask_ptr;
		
			int src_x = src_point.x;
			for( x = 0; x < blt_size.x; ++x )
			{
				// create an alpha blended pixel using the shroud mask
				// and assign it to the destination buffer
				*dest_ptr = alpha_blend16( *dest_ptr, *src_ptr++, *mask_ptr++ );
				++dest_ptr;

				++src_x;
				if ( src_x >= src_width )
				{
					src_x = 0;
					src_ptr -= src_width;
				}

			}

			++src_y;
			if ( src_y >= src_height )
			{
				src_y = 0;
				line_src_ptr = src.get_data_ptr() + src_point.x;
			}
			else
				line_src_ptr = src.advance_line( line_src_ptr );

			line_dest_ptr = dest.advance_line( line_dest_ptr );
			line_mask_ptr = shroud_mask.advance_line( line_mask_ptr );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_bitmap_layer_draw_to_func struct

	struct t_bitmap_layer_draw_to_func
	{
		void	operator()(
					t_bitmap_layer const &	layer,
					t_screen_rect const &	source_rect,
					t_abstract_bitmap16 &	dest,
					t_screen_point const &	dest_point ) const
		{
			layer.draw_to( source_rect, dest, dest_point );
		}
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_bitmap_layer draw_to_with_alpha_func class

	class t_bitmap_layer_draw_to_with_alpha_func
	{
	public:
		// Constructor
		t_bitmap_layer_draw_to_with_alpha_func( int alpha );

		// Operator
		void	operator()(
					t_bitmap_layer const &	layer,
					t_screen_rect const &	source_rect,
					t_abstract_bitmap16 &	dest,
					t_screen_point const &	dest_point ) const;

	private:
		// Data members
		int	m_alpha;
	};

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_layer_draw_to_with_alpha_func::t_bitmap_layer_draw_to_with_alpha_func(
		int	alpha )
		:	m_alpha( alpha )
	{
	}

	/////////////////////////////////////////////////////////////////////////////
	inline void t_bitmap_layer_draw_to_with_alpha_func::operator()(
		t_bitmap_layer const &	layer,
		t_screen_rect const &	source_rect,
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point ) const
	{
		layer.draw_to( source_rect, dest, dest_point, m_alpha );
	}

	/////////////////////////////////////////////////////////////////////////////
	// Non member functions

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_draw_to_func >
	void wrap_draw_helper(
		t_abstract_bitmap16 &	dest,
		t_screen_point			dest_point,
		t_bitmap_layer const &	source,
		t_screen_point			source_point,
		t_screen_point			draw_size,
		t_draw_to_func			draw_to_func )
	{
		assert( source.get_rect().left == 0 );
		assert( source.get_rect().top == 0);

		t_screen_point source_size( source.get_width(), source.get_height() );
		assert( source_size.x > 0 );
		assert( source_size.y > 0 );

		if ( source_point.x >= 0 )
			source_point.x %= source_size.x;
		else
			source_point.x = source_size.x - ( ( ( -source_point.x - 1 ) % source_size.x ) + 1 );

		if ( source_point.y >= 0 )
			source_point.y %= source_size.y;
		else
			source_point.y = source_size.y - ( ( ( -source_point.y - 1 ) % source_size.y ) + 1 );

		for (;;)
		{
			int draw_height = draw_size.y;
			if ( draw_height > source_size.y - source_point.y )
				draw_height = source_size.y - source_point.y;

			int line_source_x = source_point.x;
			int line_dest_x = dest_point.x;
			int line_draw_width = draw_size.x;

			for (;;)
			{
				int draw_width = line_draw_width;
				if ( draw_width > source_size.x - line_source_x )
					draw_width = source_size.x - line_source_x;

				draw_to_func(
					source,
					t_screen_rect( t_screen_point( line_source_x, source_point.y ), t_screen_point( draw_width, draw_height ) ),
					dest,
					t_screen_point( line_dest_x, dest_point.y ) );

				line_draw_width -= draw_width;
				if ( line_draw_width <= 0 )
					break;

				line_dest_x += draw_width;
				line_source_x = 0;
			}

			draw_size.y -= draw_height;
			if ( draw_size.y <= 0 )
				break;

			dest_point.y += draw_height;
			source_point.y = 0;
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	inline void wrap_draw(
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_point const &	draw_size )
	{
		wrap_draw_helper(
			dest,
			dest_point,
			source,
			source_point,
			draw_size,
			t_bitmap_layer_draw_to_func() );
	}

	/////////////////////////////////////////////////////////////////////////////
	inline void wrap_draw(
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	dest_point,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_point const &	draw_size,
		int						alpha )
	{
		wrap_draw_helper(
			dest,
			dest_point,
			source,
			source_point,
			draw_size,
			t_bitmap_layer_draw_to_with_alpha_func( alpha ) );
	}

	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_top_left_helper(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect,
		int						steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if (	actual_rect.width() <= 0
			||	actual_rect.height() <= 0
			||	!intersect( clip_rect, actual_rect ) )
			return;

		// Draw the bottom left quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	draw_rect(
							actual_rect.left,
							actual_rect.top,
							horizontal_sum >> ( steps + 1 ),
							vertical_sum >> ( steps + 1 ) );

		if ( intersect( draw_rect, clip_rect ) )
		{
			t_screen_rect clipped_draw_rect = intersection( draw_rect, clip_rect );
			t_screen_point actual_source_point = ( source_point >> steps ) + ( clipped_draw_rect.top_left() - draw_rect.top_left() );

			wrap_draw(
				dest,
				clipped_draw_rect.top_left(),
				source,
				actual_source_point,
				clipped_draw_rect.size() );
		}

		// Recursively draw the top left of the bottom left quarter
		draw_rect.left = dest_rect.left << 1;
		draw_rect.top = vertical_sum;
		draw_rect.right = horizontal_sum;
		draw_rect.bottom = dest_rect.bottom << 1;
		wrap_draw_top_left_helper(
			dest, draw_rect, source, ( source_point << 1 ) + t_screen_point( 0, dest_rect.height() ), clip_rect, steps + 1 );

		// Recursively draw the top left of the top right quarter
		draw_rect.left = horizontal_sum;
		draw_rect.top = dest_rect.top << 1;
		draw_rect.right = dest_rect.right << 1;
		draw_rect.bottom = vertical_sum;
		wrap_draw_top_left_helper(
			dest, draw_rect, source, ( source_point << 1 ) + t_screen_point( dest_rect.width(), 0 ), clip_rect, steps + 1 );
	}
	
	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_top_left(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect )
	{
		wrap_draw_top_left_helper( dest, dest_rect, source, source_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_top_right_helper(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect,
		int						steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if (	actual_rect.width() <= 0
			||	actual_rect.height() <= 0
			||	!intersect( clip_rect, actual_rect ) )
			return;

		// Draw the top right quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	draw_rect(
							horizontal_sum >> ( steps + 1 ),
							actual_rect.top,
							actual_rect.right,
							vertical_sum >> ( steps + 1 ) );

		if ( intersect( draw_rect, clip_rect ) )
		{
			t_screen_rect clipped_draw_rect = intersection( draw_rect, clip_rect );
			t_screen_point actual_source_point = ( source_point >> steps ) + ( clipped_draw_rect.top_left() - draw_rect.top_left() );

			wrap_draw(
				dest,
				clipped_draw_rect.top_left(),
				source,
				actual_source_point,
				clipped_draw_rect.size() );
		}

		// Recursively draw the top right of the bottom right quarter
		draw_rect.left = horizontal_sum;
		draw_rect.top = vertical_sum;
		draw_rect.right = dest_rect.right << 1;
		draw_rect.bottom = dest_rect.bottom << 1;
		wrap_draw_top_right_helper(
			dest, draw_rect, source, ( source_point << 1 ) + dest_rect.size(), clip_rect, steps + 1 );

		// Recursively draw the top right of the top left quarter
		draw_rect.left = dest_rect.left << 1;
		draw_rect.top = dest_rect.top << 1;
		draw_rect.right = horizontal_sum;
		draw_rect.bottom = vertical_sum;
		wrap_draw_top_right_helper(
			dest, draw_rect, source, source_point << 1, clip_rect, steps + 1 );
	}
	
	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_top_right(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect )
	{
		wrap_draw_top_right_helper( dest, dest_rect, source, source_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_bottom_left_helper(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect,
		int						steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if (	actual_rect.width() <= 0
			||	actual_rect.height() <= 0
			||	!intersect( clip_rect, actual_rect ) )
			return;

		// Draw the bottom left quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	draw_rect(
							actual_rect.left,
							vertical_sum >> ( steps + 1 ),
							horizontal_sum >> ( steps + 1 ),
							actual_rect.bottom );

		if ( intersect( draw_rect, clip_rect ) )
		{
			t_screen_rect clipped_draw_rect = intersection( draw_rect, clip_rect );
			t_screen_point actual_source_point = ( source_point >> steps ) + ( clipped_draw_rect.top_left() - draw_rect.top_left() );

			wrap_draw(
				dest,
				clipped_draw_rect.top_left(),
				source,
				actual_source_point,
				clipped_draw_rect.size() );
		}

		// Recursively draw the bottom left of the upper left quarter
		draw_rect.left = dest_rect.left << 1;
		draw_rect.top = dest_rect.top << 1;
		draw_rect.right = horizontal_sum;
		draw_rect.bottom = vertical_sum;
		wrap_draw_bottom_left_helper(
			dest, draw_rect, source, source_point << 1, clip_rect, steps + 1 );

		// Recursively draw the bottom left of the bottom right quarter
		draw_rect.left = horizontal_sum;
		draw_rect.top = vertical_sum;
		draw_rect.right = dest_rect.right << 1;
		draw_rect.bottom = dest_rect.bottom << 1;
		wrap_draw_bottom_left_helper(
			dest, draw_rect, source, ( source_point << 1 ) + dest_rect.size(), clip_rect, steps + 1 );
	}
	
	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_bottom_left(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect )
	{
		wrap_draw_bottom_left_helper( dest, dest_rect, source, source_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_bottom_right_helper(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect,
		int						steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if (	actual_rect.width() <= 0
			||	actual_rect.height() <= 0
			||	!intersect( clip_rect, actual_rect ) )
			return;

		// Draw the bottom right quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	draw_rect(
							horizontal_sum >> ( steps + 1 ),
							vertical_sum >> ( steps + 1 ),
							actual_rect.right,
							actual_rect.bottom );

		if ( intersect( draw_rect, clip_rect ) )
		{
			t_screen_rect clipped_draw_rect = intersection( draw_rect, clip_rect );
			t_screen_point actual_source_point = ( source_point >> steps ) + ( clipped_draw_rect.top_left() - draw_rect.top_left() );

			wrap_draw(
				dest,
				clipped_draw_rect.top_left(),
				source,
				actual_source_point,
				clipped_draw_rect.size() );
		}

		// Recursively draw the bottom right of the upper right quarter
		draw_rect.left = horizontal_sum;
		draw_rect.top = dest_rect.top << 1;
		draw_rect.right = dest_rect.right << 1;
		draw_rect.bottom = vertical_sum;
		wrap_draw_bottom_right_helper(
			dest, draw_rect, source, ( source_point << 1 ) + t_screen_point( dest_rect.width(), 0 ), clip_rect, steps + 1 );

		// Recursively draw the bottom right of the bottom left quarter
		draw_rect.left = dest_rect.left << 1;
		draw_rect.top = vertical_sum;
		draw_rect.right = horizontal_sum;
		draw_rect.bottom = dest_rect.bottom << 1;
		wrap_draw_bottom_right_helper(
			dest, draw_rect, source, ( source_point << 1 ) + t_screen_point( 0, dest_rect.height() ), clip_rect, steps + 1 );
	}
	
	/////////////////////////////////////////////////////////////////////////////
	void wrap_draw_bottom_right(
		t_abstract_bitmap16 &	dest,
		t_screen_rect const &	dest_rect,
		t_bitmap_layer const &	source,
		t_screen_point const &	source_point,
		t_screen_rect const &	clip_rect )
	{
		wrap_draw_bottom_right_helper( dest, dest_rect, source, source_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void scroll( t_abstract_bitmap< t_pixel > & bmp, t_screen_rect const & rect, t_screen_point const & delta )
	{
		t_screen_point abs_delta( delta.x >= 0 ? delta.x : -delta.x, delta.y >= 0 ? delta.y : -delta.y );
		t_screen_rect clip_rect = intersection( rect, t_screen_rect( 0, 0, bmp.get_width(), bmp.get_height() ) );
		t_screen_point scroll_size = clip_rect.size() - abs_delta;
		if ( scroll_size.x <= 0 || scroll_size.y <= 0 )
			return;

		int src_x;
		int dest_x;

		if ( delta.x >= 0 )
		{
			src_x = clip_rect.left;
			dest_x = clip_rect.right - scroll_size.x;
		}
		else
		{
			src_x = clip_rect.right - scroll_size.x;
			dest_x = clip_rect.left;
		}

		size_t const line_copy_size = scroll_size.x * sizeof( t_pixel );

		t_pixel * dest_ptr;
		t_pixel const * src_ptr;

		if ( delta.y > 0 )
		{
			// Copy from the bottom up
			int src_y = clip_rect.top;
			int dest_y = clip_rect.bottom - scroll_size.y;

			dest_ptr = bmp.advance_line( bmp.get_data_ptr(), dest_y + scroll_size.y ) + dest_x;
			src_ptr = bmp.advance_line( bmp.get_data_ptr(), src_y + scroll_size.y ) + src_x;

			int i;
			for ( i = 0; i < scroll_size.y; ++i )
			{
				dest_ptr = bmp.advance_line( dest_ptr, -1 );
				src_ptr = bmp.advance_line( src_ptr, -1 );

				memcpy( dest_ptr, src_ptr, line_copy_size );
			}
		}
		else if ( delta.y < 0 )
		{
			// Copy from the top down
			int src_y = clip_rect.bottom - scroll_size.y;
			int dest_y = clip_rect.top;

			dest_ptr = bmp.advance_line( bmp.get_data_ptr(), dest_y ) + dest_x;
			src_ptr = bmp.advance_line( bmp.get_data_ptr(), src_y ) + src_x;

			int i;
			for ( i = 0; i < scroll_size.y; ++i )
			{
				memcpy( dest_ptr, src_ptr, line_copy_size );

				dest_ptr = bmp.advance_line( dest_ptr );
				src_ptr = bmp.advance_line( src_ptr );
			}
		}
		else
		{
			// Copy from the top down
			int src_y = clip_rect.top;
			int dest_y = clip_rect.top;

			dest_ptr = bmp.advance_line( bmp.get_data_ptr(), dest_y ) + dest_x;
			src_ptr = bmp.advance_line( bmp.get_data_ptr(), src_y ) + src_x;

			// If the source and destination overlap we need to use memmove
			if ( scroll_size.x * 2 > clip_rect.width() )
			{
				int i;
				for ( i = 0; i < scroll_size.y; ++i )
				{
					memmove( dest_ptr, src_ptr, line_copy_size );

					dest_ptr = bmp.advance_line( dest_ptr );
					src_ptr = bmp.advance_line( src_ptr );
				}
			}
			else
			{
				int i;
				for ( i = 0; i < scroll_size.y; ++i )
				{
					memcpy( dest_ptr, src_ptr, line_copy_size );

					dest_ptr = bmp.advance_line( dest_ptr );
					src_ptr = bmp.advance_line( src_ptr );
				}
			}
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void fill_top_left_helper(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Fill the lower left quarter of the rect
		int horizontal_sum = rect.left + rect.right;
		int vertical_sum = rect.top + rect.bottom;
		t_screen_rect	fill_rect(
							actual_rect.left,
							actual_rect.top,
							horizontal_sum >> ( steps + 1 ),
							vertical_sum >> ( steps + 1 ) );

		if ( intersect( fill_rect, clip_rect ) )
			fill( bitmap, intersection( fill_rect, clip_rect ), color );

		// Recursively fill the top left of the upper right quarter
		fill_rect.left = horizontal_sum;
		fill_rect.top = rect.top << 1;
		fill_rect.right = rect.right << 1;
		fill_rect.bottom = vertical_sum;
		fill_top_left_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );

		// Recursively fill the top left of the bottom left quarter
		fill_rect.left = rect.left << 1;
		fill_rect.top = vertical_sum;
		fill_rect.right = horizontal_sum;
		fill_rect.bottom = rect.bottom << 1;
		fill_top_left_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void fill_top_left(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect )
	{
		fill_top_left_helper( bitmap, rect, color, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void fill_top_right_helper(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Fill the lower left quarter of the rect
		int horizontal_sum = rect.left + rect.right;
		int vertical_sum = rect.top + rect.bottom;
		t_screen_rect	fill_rect(
							horizontal_sum >> ( steps + 1 ),
							actual_rect.top,
							actual_rect.right,
							vertical_sum >> ( steps + 1 ) );

		if ( intersect( fill_rect, clip_rect ) )
			fill( bitmap, intersection( fill_rect, clip_rect ), color );

		// Recursively fill the top right of the upper left quarter
		fill_rect.left = rect.left << 1;
		fill_rect.top = rect.top << 1;
		fill_rect.right = horizontal_sum;
		fill_rect.bottom = vertical_sum;
		fill_top_right_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );

		// Recursively fill the top right of the bottom right quarter
		fill_rect.left = horizontal_sum;
		fill_rect.top = vertical_sum;
		fill_rect.right = rect.right << 1;
		fill_rect.bottom = rect.bottom << 1;
		fill_top_right_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void fill_top_right(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect )
	{
		fill_top_right_helper( bitmap, rect, color, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void fill_bottom_left_helper(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Fill the lower left quarter of the rect
		int horizontal_sum = rect.left + rect.right;
		int vertical_sum = rect.top + rect.bottom;
		t_screen_rect	fill_rect(
							actual_rect.left,
							vertical_sum >> ( steps + 1 ),
							horizontal_sum >> ( steps + 1 ),
							actual_rect.bottom );

		if ( intersect( fill_rect, clip_rect ) )
			fill( bitmap, intersection( fill_rect, clip_rect ), color );

		// Recursively fill the bottom left of the upper left quarter
		fill_rect.left = rect.left << 1;
		fill_rect.top = rect.top << 1;
		fill_rect.right = horizontal_sum;
		fill_rect.bottom = vertical_sum;
		fill_bottom_left_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );

		// Recursively fill the bottom left of the bottom right quarter
		fill_rect.left = horizontal_sum;
		fill_rect.top = vertical_sum;
		fill_rect.right = rect.right << 1;
		fill_rect.bottom = rect.bottom << 1;
		fill_bottom_left_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void fill_bottom_left(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect )
	{
		fill_bottom_left_helper( bitmap, rect, color, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void fill_bottom_right_helper(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Fill the lower left quarter of the rect
		int horizontal_sum = rect.left + rect.right;
		int vertical_sum = rect.top + rect.bottom;
		t_screen_rect	fill_rect(
							horizontal_sum >> ( steps + 1 ),
							vertical_sum >> ( steps + 1 ),
							actual_rect.right,
							actual_rect.bottom );

		if ( intersect( fill_rect, clip_rect ) )
			fill( bitmap, intersection( fill_rect, clip_rect ), color );

		// Recursively fill the bottom right of the upper right quarter
		fill_rect.left = horizontal_sum;
		fill_rect.top = rect.top << 1;
		fill_rect.right = rect.right << 1;
		fill_rect.bottom = vertical_sum;
		fill_bottom_right_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );

		// Recursively fill the bottom right of the bottom left quarter
		fill_rect.left = rect.left << 1;
		fill_rect.top = vertical_sum;
		fill_rect.right = horizontal_sum;
		fill_rect.bottom = rect.bottom << 1;
		fill_bottom_right_helper( bitmap, fill_rect, color, clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void fill_bottom_right(
		t_abstract_bitmap< t_pixel > &	bitmap,
		t_screen_rect const &			rect,
		t_pixel							color,
		t_screen_rect const &			clip_rect )
	{
		fill_bottom_right_helper( bitmap, rect, color, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void bit_blt_horizontal_source_top_left_helper(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Blt the top left quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	blt_rect(
							actual_rect.left,
							actual_rect.top,
							horizontal_sum >> ( steps + 1 ),
							vertical_sum >> ( steps + 1 ) );

		if ( intersect( blt_rect, clip_rect ) )
		{
			t_screen_rect clipped_blt_rect = intersection( blt_rect, clip_rect );
			int actual_src_point = ( src_point >> steps ) + ( clipped_blt_rect.left - blt_rect.left );

			bit_blt_horizontal_source(
				dest,
				clipped_blt_rect.top_left(),
				src,
				actual_src_point,
				clipped_blt_rect.size() );
		}

		// Recursively blt the top left of the upper right quarter
		blt_rect.left = horizontal_sum;
		blt_rect.top = dest_rect.top << 1;
		blt_rect.right = dest_rect.right << 1;
		blt_rect.bottom = vertical_sum;
		bit_blt_horizontal_source_top_left_helper( dest, blt_rect, src, ( src_point << 1 ) + dest_rect.width(), clip_rect, steps + 1 );

		// Recursively blt the top left of the bottom left quarter
		blt_rect.left = dest_rect.left << 1;
		blt_rect.top = vertical_sum;
		blt_rect.right = horizontal_sum;
		blt_rect.bottom = dest_rect.bottom << 1;
		bit_blt_horizontal_source_top_left_helper( dest, blt_rect, src, src_point << 1, clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void bit_blt_horizontal_source_top_left(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect )
	{
		bit_blt_horizontal_source_top_left_helper( dest, dest_rect, src, src_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void bit_blt_horizontal_source_top_right_helper(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Blt the top right quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	blt_rect(
							horizontal_sum >> ( steps + 1 ),
							actual_rect.top,
							actual_rect.right,
							vertical_sum >> ( steps + 1 ) );

		if ( intersect( blt_rect, clip_rect ) )
		{
			t_screen_rect clipped_blt_rect = intersection( blt_rect, clip_rect );
			int actual_src_point = ( ( src_point + ( dest_rect.width() >> 1 ) ) >> steps ) + ( clipped_blt_rect.left - blt_rect.left );

			bit_blt_horizontal_source(
				dest,
				clipped_blt_rect.top_left(),
				src,
				actual_src_point,
				clipped_blt_rect.size() );
		}

		// Recursively blt the top right of the upper left quarter
		blt_rect.left = dest_rect.left << 1;
		blt_rect.top = dest_rect.top << 1;
		blt_rect.right = horizontal_sum;
		blt_rect.bottom = vertical_sum;
		bit_blt_horizontal_source_top_right_helper( dest, blt_rect, src, src_point << 1, clip_rect, steps + 1 );

		// Recursively blt the top right of the bottom right quarter
		blt_rect.left = horizontal_sum;
		blt_rect.top = vertical_sum;
		blt_rect.right = dest_rect.right << 1;
		blt_rect.bottom = dest_rect.bottom << 1;
		bit_blt_horizontal_source_top_right_helper( dest, blt_rect, src, ( src_point << 1 ) + dest_rect.width(), clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void bit_blt_horizontal_source_top_right(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect )
	{
		bit_blt_horizontal_source_top_right_helper( dest, dest_rect, src, src_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void bit_blt_horizontal_source_bottom_left_helper(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Blt the bottom left quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	blt_rect(
							actual_rect.left,
							vertical_sum >> ( steps + 1 ),
							horizontal_sum >> ( steps + 1 ),
							actual_rect.bottom );

		if ( intersect( blt_rect, clip_rect ) )
		{
			t_screen_rect clipped_blt_rect = intersection( blt_rect, clip_rect );
			int actual_src_point = ( src_point >> steps ) + ( clipped_blt_rect.left - blt_rect.left );

			bit_blt_horizontal_source(
				dest,
				clipped_blt_rect.top_left(),
				src,
				actual_src_point,
				clipped_blt_rect.size() );
		}

		// Recursively blt the bottom left of the upper left quarter
		blt_rect.left = dest_rect.left << 1;
		blt_rect.top = dest_rect.top << 1;
		blt_rect.right = horizontal_sum;
		blt_rect.bottom = vertical_sum;
		bit_blt_horizontal_source_bottom_left_helper( dest, blt_rect, src, src_point << 1, clip_rect, steps + 1 );

		// Recursively blt the bottom left of the bottom right quarter
		blt_rect.left = horizontal_sum;
		blt_rect.top = vertical_sum;
		blt_rect.right = dest_rect.right << 1;
		blt_rect.bottom = dest_rect.bottom << 1;
		bit_blt_horizontal_source_bottom_left_helper( dest, blt_rect, src, ( src_point << 1 ) + dest_rect.width(), clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void bit_blt_horizontal_source_bottom_left(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect )
	{
		bit_blt_horizontal_source_bottom_left_helper( dest, dest_rect, src, src_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	void bit_blt_horizontal_source_bottom_right_helper(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect,
		int								steps )
	{
		t_screen_rect actual_rect = dest_rect >> steps;

		if ( actual_rect.width() <= 0 || actual_rect.height() <= 0 || !intersect( clip_rect, actual_rect ) )
			return;

		// Blt the bottom right quarter of the rect
		int horizontal_sum = dest_rect.left + dest_rect.right;
		int vertical_sum = dest_rect.top + dest_rect.bottom;
		t_screen_rect	blt_rect(
							horizontal_sum >> ( steps + 1 ),
							vertical_sum >> ( steps + 1 ),
							actual_rect.right,
							actual_rect.bottom );

		if ( intersect( blt_rect, clip_rect ) )
		{
			t_screen_rect clipped_blt_rect = intersection( blt_rect, clip_rect );
			int actual_src_point = ( ( src_point + ( dest_rect.width() >> 1 ) ) >> steps ) + ( clipped_blt_rect.left - blt_rect.left );

			bit_blt_horizontal_source(
				dest,
				clipped_blt_rect.top_left(),
				src,
				actual_src_point,
				clipped_blt_rect.size() );
		}

		// Recursively blt the bottom right of the upper right quarter
		blt_rect.left = horizontal_sum;
		blt_rect.top = dest_rect.top << 1;
		blt_rect.right = dest_rect.right << 1;
		blt_rect.bottom = vertical_sum;
		bit_blt_horizontal_source_bottom_right_helper( dest, blt_rect, src, ( src_point << 1 ) + dest_rect.width(), clip_rect, steps + 1 );

		// Recursively blt the bottom right of the bottom left quarter
		blt_rect.left = dest_rect.left << 1;
		blt_rect.top = vertical_sum;
		blt_rect.right = horizontal_sum;
		blt_rect.bottom = dest_rect.bottom << 1;
		bit_blt_horizontal_source_bottom_right_helper( dest, blt_rect, src, src_point << 1, clip_rect, steps + 1 );
	}

	/////////////////////////////////////////////////////////////////////////////
	template < typename t_pixel >
	inline void bit_blt_horizontal_source_bottom_right(
		t_abstract_bitmap< t_pixel > &	dest,
		t_screen_rect const &			dest_rect,
		t_bitmap_1d< t_pixel > const &	src,
		int								src_point,
		t_screen_rect const &			clip_rect )
	{
		bit_blt_horizontal_source_bottom_right_helper( dest, dest_rect, src, src_point, clip_rect, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	struct t_tile_passability_info
	{
		bool		is_impassable;
		struct
		{
			bool	top_left;
			bool	top_right;
			bool	bottom_left;
			bool	bottom_right;
		}			is_trigger;
	};

	/////////////////////////////////////////////////////////////////////////////
	void compute_tile_passability_info(
		t_abstract_adventure_map const &	map,
		t_level_map_point_2d const &		tile_point,
		t_tile_passability_info &			result )
	{
		result.is_impassable = false;
		result.is_trigger.top_left = false;
		result.is_trigger.top_right = false;
		result.is_trigger.bottom_left = false;
		result.is_trigger.bottom_right = false;

		typedef t_abstract_adventure_map::t_tile		t_tile;
		typedef t_abstract_adventure_map::t_object		t_object;
		typedef t_abstract_adventure_map::t_object_id	t_object_id;

		t_tile const & tile = map.get_tile( tile_point );

		// Lava rivers are always impassable
		if ( tile.get_terrain() == k_terrain_lava_river )
			result.is_impassable = true;

		int count = tile.get_intersecting_object_count();
		int index;
		for ( index = 0; index < count; ++index )
		{
			t_object_id object_id = tile.get_intersecting_object_id( index );
			t_footprint const & object_footprint = map.get_object( object_id ).get_footprint();
			t_map_point_2d object_cell_pos = tile_point - map.get_object_pos( object_id );

			bool impassable = object_footprint.is_cell_impassable( object_cell_pos );

			// If this cell of this object is impassable make sure all of the
			// trigger edges from previous objects are reset.
			if ( impassable )
			{
				result.is_trigger.top_left = false;
				result.is_trigger.top_right = false;
				result.is_trigger.bottom_left = false;
				result.is_trigger.bottom_right = false;
			}

			// Record this cell's trigger edges only if this cell hasn't been
			// marked as impassable by previous objects
			if ( !result.is_impassable )
			{
				result.is_trigger.top_left =
					result.is_trigger.top_left || object_footprint.is_left_edge_trigger( object_cell_pos );
				result.is_trigger.top_right =
					result.is_trigger.top_right || object_footprint.is_right_edge_trigger( object_cell_pos );
				++object_cell_pos.row;
				result.is_trigger.bottom_left =
					result.is_trigger.bottom_left || object_footprint.is_right_edge_trigger( object_cell_pos );
				--object_cell_pos.row;
				++object_cell_pos.column;
				result.is_trigger.bottom_right =
					result.is_trigger.bottom_right || object_footprint.is_left_edge_trigger( object_cell_pos );
			}

			result.is_impassable = result.is_impassable || impassable;
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	t_uint16 g_impassable_color;
	t_uint16 g_trigger_color;

	/////////////////////////////////////////////////////////////////////////////
	// t_passability_color_maintainer class - responsible for
	// maintaining g_impassable_color and g_trigger_color

	class t_passability_color_maintainer : public t_pixel_mask_viewer
	{
	public:
		// Constructor
		t_passability_color_maintainer();

		// Member functions
		virtual void	on_pixel_masks_changed();

	private:
		// Member functions
		void	update_passability_colors();
	};

	/////////////////////////////////////////////////////////////////////////////
	// t_passability_color_maintainer members

	/////////////////////////////////////////////////////////////////////////////
	inline t_passability_color_maintainer::t_passability_color_maintainer()
	{
		update_passability_colors();
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_passability_color_maintainer::on_pixel_masks_changed()
	{
		update_passability_colors();
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_passability_color_maintainer::update_passability_colors()
	{
		// Impassable cells are shaded red
		g_impassable_color = convert_to_16_bit( 255, 0, 0 );

		// Trigger edges are shaded yellow
		g_trigger_color = convert_to_16_bit( 255, 255, 0 );
	}

	/////////////////////////////////////////////////////////////////////////////
	void draw_tile_passability(
		t_abstract_adventure_map const &	map,
		t_level_map_point_2d const &		tile_point,
		t_abstract_bitmap16 &				dest,
		t_screen_point const &				screen_pos,
		t_screen_rect const &				clip_rect )
	{
		static t_passability_color_maintainer color_maintainer;

		t_tile_passability_info passability_info;
		compute_tile_passability_info(
			map,
			tile_point,
			passability_info );

		// Compute the shading for each quarter of the tile
		enum t_shade
		{
			k_shade_none,
			k_shade_impassable,
			k_shade_trigger,
		};

		t_shade non_trigger_shade = passability_info.is_impassable ? k_shade_impassable : k_shade_none;
		t_shade top_left_shade = passability_info.is_trigger.top_left ? k_shade_trigger : non_trigger_shade;
		t_shade top_right_shade = passability_info.is_trigger.top_right ? k_shade_trigger : non_trigger_shade;
		t_shade bottom_left_shade = passability_info.is_trigger.bottom_left ? k_shade_trigger : non_trigger_shade;
		t_shade bottom_right_shade = passability_info.is_trigger.bottom_right ? k_shade_trigger : non_trigger_shade;

		// Stop now if there is no shading
		if (	top_left_shade == k_shade_none
			&&	top_right_shade == k_shade_none
			&&	bottom_left_shade == k_shade_none
			&&	bottom_right_shade == k_shade_none )
			return;

		// Get the tile vertex height quad for this tile
		t_abstract_adventure_map::t_const_tile_vertex_quad tile_vertex_quad =
			map.get_tile_vertex_quad( tile_point );
		t_quad< int >	height_quad(
							tile_vertex_quad.left.get_height(),
							tile_vertex_quad.top.get_height(),
							tile_vertex_quad.right.get_height(),
							tile_vertex_quad.bottom.get_height() );

		// Do the shading

		// Check for uniform shading
		if (	top_right_shade == top_left_shade
			&&	bottom_left_shade == top_left_shade
			&&	bottom_right_shade == top_left_shade )
		{
			// The entire tile is shaded uniformly
			assert( top_left_shade != k_shade_none );

			shade_adventure_tile(
				top_left_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
				dest,
				screen_pos,
				clip_rect,
				height_quad );
			return;
		}

		// Shade left half
		if ( top_left_shade == bottom_left_shade )
		{
			if ( top_left_shade != k_shade_none )
			{
				shade_adventure_tile_half(
					top_left_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
					dest,
					screen_pos,
					clip_rect,
					height_quad,
					true );
			}
		}
		else
		{
			// Shade top left quarter
			if ( top_left_shade != k_shade_none )
			{
				shade_adventure_tile_quarter(
					top_left_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
					dest,
					screen_pos,
					clip_rect,
					height_quad,
					true,
					true );
			}
			
			// Shade bottom left quarter
			if ( bottom_left_shade != k_shade_none )
			{
				shade_adventure_tile_quarter(
					bottom_left_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
					dest,
					screen_pos,
					clip_rect,
					height_quad,
					true,
					false );
			}
		}

		// Shade right half
		if ( top_right_shade == bottom_right_shade )
		{
			if ( top_right_shade != k_shade_none )
			{
				shade_adventure_tile_half(
					top_right_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
					dest,
					screen_pos,
					clip_rect,
					height_quad,
					false );
			}
		}
		else
		{
			// Shade top right quarter
			if ( top_right_shade != k_shade_none )
			{
				shade_adventure_tile_quarter(
					top_right_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
					dest,
					screen_pos,
					clip_rect,
					height_quad,
					false,
					true );
			}
			
			// Shade bottom right quarter
			if ( bottom_right_shade != k_shade_none )
			{
				shade_adventure_tile_quarter(
					bottom_right_shade == k_shade_impassable ? g_impassable_color : g_trigger_color,
					dest,
					screen_pos,
					clip_rect,
					height_quad,
					false,
					false );
			}
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_uint8 get_visibility_alpha( t_tile_visibility visibility )
	{
		assert( visibility >= 0 && visibility < k_tile_visibility_count );

		static t_uint8 const k_visibility_alphas[ k_tile_visibility_count ] =
		{
			15,	// Not explored
			8,	// Not visible
			0,	// Visible
		};

		return k_visibility_alphas[ visibility ];
	}

	/////////////////////////////////////////////////////////////////////////////
	void draw_tile_shroud_mask(
		t_map_renderer::t_adv_map const &	adventure_map,
		int									team_view,
		t_level_map_point_2d const &		tile_point,
		t_quad< int > const &				height_quad,
		t_abstract_bitmap8 &				shroud_mask,
		t_screen_point const &				screen_pos,
		t_screen_rect const &				clip_rect )
	{
		typedef t_map_renderer::t_adv_map t_adv_map;

		t_adv_map::t_tile const & tile = adventure_map.get_tile( tile_point );

		t_uint8 background_alpha = get_visibility_alpha( tile.get_visibility( team_view ) );

		t_shroud_transition_map const * transition_map_ptr =
			adventure_map.get_shroud_transition_map_ptr( team_view );

		if ( transition_map_ptr != 0 )
		{
			typedef t_shroud_transition_map::t_transition_list t_transition_list;
			t_transition_list const & transitions =
				transition_map_ptr->get_transitions( tile_point );

			if ( transitions.empty() )
				fill_adventure_tile( background_alpha, shroud_mask, screen_pos, clip_rect, height_quad );
			else
			{
				// Draw each transition
				t_transition_list::const_iterator transition_iter = transitions.begin();

				// Draw the first transition
				{
					t_shroud_transition const & transition = *transition_iter;

					t_uint8 foreground_alpha = get_visibility_alpha( transition.get_visibility() );
					t_transition_mask const & mask =
						g_transition_masks[ transition.get_mask_set() ][ transition.get_id() ];

					fill_adventure_tile_alpha_mask(
						foreground_alpha,
						background_alpha,
						shroud_mask,
						screen_pos,
						clip_rect,
						height_quad,
						mask );
				}

				// Compose the subsequent transitions
				t_transition_list::const_iterator transition_end = transitions.end();
				for ( ++transition_iter; transition_iter != transition_end; ++transition_iter )
				{
					t_shroud_transition const & transition = *transition_iter;

					t_uint8 foreground_alpha = get_visibility_alpha( transition.get_visibility() );
					t_transition_mask const & mask =
						g_transition_masks[ transition.get_mask_set() ][ transition.get_id() ];

					compose_adventure_tile_alpha_mask(
						foreground_alpha,
						shroud_mask,
						screen_pos,
						clip_rect,
						height_quad,
						mask );
				}
			}
		}
		else
			fill_adventure_tile( background_alpha, shroud_mask, screen_pos, clip_rect, height_quad );
	}

	/////////////////////////////////////////////////////////////////////////////
	void draw_tile_shroud_mask(
		t_map_renderer::t_adv_map const &	adventure_map,
		int									team_view,
		t_level_map_point_2d const &		tile_point,
		t_abstract_bitmap8 &				shroud_mask,
		t_screen_point const &				screen_pos,
		t_screen_rect const &				clip_rect )
	{
		
		t_map_renderer::t_adv_map::t_const_tile_vertex_quad tile_vertex_quad =
			adventure_map.get_tile_vertex_quad( tile_point );
		t_quad< int >	height_quad(
							tile_vertex_quad.left.get_height(),
							tile_vertex_quad.top.get_height(),
							tile_vertex_quad.right.get_height(),
							tile_vertex_quad.bottom.get_height() );

		draw_tile_shroud_mask(
			adventure_map,
			team_view,
			tile_point,
			height_quad,
			shroud_mask,
			screen_pos,
			clip_rect );
	}

	/////////////////////////////////////////////////////////////////////////////
	// t_bitmap_1d_to_2d_horizontal_adapter class template

	template < typename t_pixel_arg >
	class t_bitmap_1d_to_2d_horizontal_adapter
		:	public t_abstract_bitmap< t_pixel_arg >
	{
	public:
		// Constructor
		explicit t_bitmap_1d_to_2d_horizontal_adapter(
			t_bitmap_1d< t_pixel_arg > & source );
	};

	template < typename t_pixel_arg >
	inline t_bitmap_1d_to_2d_horizontal_adapter< t_pixel_arg >::t_bitmap_1d_to_2d_horizontal_adapter(
		t_bitmap_1d< t_pixel_arg > & source )
		:	t_abstract_bitmap< t_pixel_arg >(
				source.get_size(),
				1,
				source.get_size() * sizeof( t_pixel_arg ),
				source.get_data_ptr() )
	{
	}

	/////////////////////////////////////////////////////////////////////////////
	void draw_tile_half_horizontal_shroud_mask(
		t_map_renderer::t_adv_map const &	adventure_map,
		int									team_view,
		t_level_map_point_2d const &		tile_point,
		bool								left_half,
		t_bitmap_1d< t_uint8 > &			dest )
	{
		t_bitmap_1d_to_2d_horizontal_adapter< t_uint8 > dest_adapter( dest );
		draw_tile_shroud_mask(
			adventure_map,
			team_view,
			tile_point,
			t_quad< int >( 0, 0, 0, 0 ),
			dest_adapter,
			t_screen_point( left_half ? 0 : -k_adventure_tile_width / 2, -k_adventure_tile_height / 2 + 1 ),
			t_screen_rect( 0, 0, k_adventure_tile_width / 2, 1 ) );
	}

	/////////////////////////////////////////////////////////////////////////////
	inline t_bitmap_1d< t_uint8 > & get_margin_column_shroud_mask()
	{
		static t_bitmap_1d< t_uint8 > instance( k_margin_column_width );
		return instance;
	}

} // Unnamed namespace

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer_client members

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer_client::on_rects_dirtied(
	std::vector< t_screen_rect > const & dirtied_rects )
{
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer_client::on_view_moved(
	int						new_view_level,
	t_screen_point const &	new_view_pos,
	t_screen_point const &	new_view_size )
{
}

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer::t_impl class

class t_map_renderer::t_impl : private t_idle_processor
{
public:
	// Types
	typedef t_vector_set< t_object_id >					t_object_id_set;
	typedef t_memory_bitmap< t_back_buffer::t_pixel >	t_terrain_buffer;
	typedef t_memory_bitmap8							t_shroud_mask;
	typedef t_owned_ptr< t_terrain_buffer >				t_terrain_buffer_ptr;
	typedef t_owned_ptr< t_shroud_mask>					t_shroud_mask_ptr;

	class t_draw_terrain_map_adapter;
	class t_draw_tile_shroud_mask_func;
	class t_draw_tile_passability_func;

	// Data members
	t_map_renderer &					m_outer;
	t_client *							m_client_ptr;
	t_adv_map const &					m_adventure_map;

	t_internal_map_data_map::iterator	m_internal_map_data_iter;
	t_internal_map_list_ptr				m_internal_map_list;

	t_uint32							m_last_update_time;

	t_object_id_set						m_animating_object_ids;
	bool								m_animation_enabled;

	bool								m_view_grid;

	int									m_view_level;
	t_screen_point						m_view_pos;
	t_screen_point						m_view_size;

	t_back_buffer_ptr					m_back_buffer_ptr;

	t_screen_point						m_buffer_origin;		// Note: this is shared by several buffers
	t_terrain_buffer_ptr				m_terrain_buffer_ptr;
	t_shroud_mask_ptr					m_shroud_mask_ptr;

	t_clip_list							m_dirty_list;
	t_sorted_clip_list					m_dirty_quadtree;

	int									m_team_view;
	bool								m_view_passability;

	t_clip_list							m_refresh_list;
	t_clip_list							m_intersect_list;

	t_border_frame_layers				m_border_frame_layers;
	t_margin_texture_layers				m_margin_texture_layers;

	// Constructor
	t_impl(
		t_map_renderer &	outer,
		t_client *			client_ptr,
		t_adv_map const &	adventure_map );
	~t_impl();

	// Member functions
	bool							add_animating_object_id( t_object_id object_id );
	void							adjust_object_animation_for_shroud_change(
										t_tile_point_set const &	tile_points,
										t_clip_list &				clip_list );
	void							adjust_object_visibility_for_visibility_change(
										t_visibility_set const &	tile_points,
										t_clip_list &				clip_list );
	t_clip_list						build_clip_list( t_tile_point_set const & tile_points ) const;
	t_clip_list						build_clip_list_for_shroud(
										t_tile_point_set const &	tile_points ) const;
	t_tile_point_set				build_map_rect_point_set(
										t_map_point_2d const &	pos,
										t_map_point_2d const &	size ) const;
	void							clear_animating_object_ids();
	void							dirty( t_screen_rect const & rect );
	void							dirty( t_clip_list const & clip_list );
	void							draw_bottom_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_bottom_left_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_bottom_margin_column(
										t_screen_rect const &	clip_rect,
										int						margin_column_num );
	void							draw_bottom_margin_column_shroud_mask(
										int						margin_column_num,
										t_screen_rect const &	rect,
										t_screen_point const &	view_pos );
	void							draw_bottom_right_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_edges_and_adjust_rect( t_screen_rect & clip_rect );
	void							draw_left_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_map( t_screen_rect const & clip_rect );
	void							draw_map_margins( t_screen_rect const & clip_rect );
	void							draw_objects( t_screen_rect const & clip_rect );
	void							draw_passability( t_screen_rect const & clip_rect );
	void							draw_right_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_shroud( t_screen_rect const & clip_rect );
	void							draw_terrain( t_screen_rect const & clip_rect );
	void							draw_to_shroud_mask( 
										t_screen_rect const &	src_rect,
										t_screen_point const &	dest_pos );
	void							draw_top_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_top_left_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							draw_top_margin_column(
										t_screen_rect const &	clip_rect,
										int						margin_column_num );
	void							draw_top_margin_column_shroud_mask(
										int						margin_column_num,
										t_screen_rect const &	rect,
										t_screen_point const &	view_pos );
	void							draw_top_right_edge(
										t_screen_rect const &	clip_rect,
										t_screen_rect const &	map_view_rect );
	void							enable_animation( bool arg );
	t_screen_point					get_adv_object_screen_point( t_object_id object_id ) const;
	t_uint32						get_last_update_time(
										t_object_id			object_id,
										t_object const &	object ) const;
	t_uint32						get_last_update_time(
										t_object_id	object_id ) const;
	t_internal_map_data &			get_internal_map_data() const;
	void							init();
	void							invalidate(
										t_object_height_delta_set const &	changed_object_height_deltas,
										t_clip_list &						result );
	void							invalidate_object(
										t_object const &				object,
										t_level_map_point_3d const &	object_pos,
										t_uint32						current_time,
										t_clip_list &					clip_list );
	void							invalidate_object(
										t_object_id		object_id,
										t_uint32		current_time,
										t_clip_list &	clip_list );
	void							invalidate_object_if_visible(
										t_object_id		object_id,
										t_uint32		current_time,
										t_clip_list &	clip_list );
	bool							is_under_fog_of_war( t_object_id object_id ) const;
	void							on_adv_object_moved( t_object_id object_id );
	void							on_adv_object_moved(
										t_object_id							object_id,
										int									level,
										t_tile_point_set const &			changed_tile_points,
										std::map< int, int > const &		top_margin_column_height_deltas,
										std::map< int, int > const &		bottom_margin_column_height_deltas,
										t_object_height_delta_set const &	changed_object_height_deltas );
	void							on_adv_object_placed( t_object_id object_id );
	void							on_adv_object_placed(
										t_object_id							object_id,
										int									level,
										t_tile_point_set const &			changed_tile_points,
										std::map< int, int > const &		top_margin_column_height_deltas,
										std::map< int, int > const &		bottom_margin_column_height_deltas,
										t_object_height_delta_set const &	changed_object_height_deltas );
	void							on_adv_object_removed(
										t_object const &				object,
										t_level_map_point_2d const &	object_pos );
	virtual void					on_idle();
	void							on_moving_adv_object( t_object_id object_id );
	void							on_multiple_adv_objects_removed();
	void							on_removing_adv_object( t_object_id object_id );
	void							on_removing_multiple_adv_objects(
										t_object_id_set const &	object_ids );
	void							on_rock_terrain_changed(
										int							level,
										t_tile_point_set const &	changed_tile_points,
										t_object_id_set const &		placed_object_ids );
	void							on_terrain_changed(
										int								level,
										t_tile_point_set const &		changed_tile_points,
										std::map< int, int > const &	top_margin_column_height_deltas,
										std::map< int, int > const &	bottom_margin_column_height_deltas );
	void							on_terrain_changed(
										int									level,
										t_tile_point_set const &			changed_tile_points,
										std::map< int, int > const &		top_margin_column_height_deltas,
										std::map< int, int > const &		bottom_margin_column_height_deltas,
										t_object_height_delta_set const &	changed_object_height_deltas );
	void							on_terrain_changed_helper(
										t_tile_point_set const &		changed_tile_points,
										std::map< int, int > const &	top_margin_column_height_deltas,
										std::map< int, int > const &	bottom_margin_column_height_deltas,
										t_clip_list &					result );
	void							on_visibility_changed(
										int							team_num,
										int							level,
										t_visibility_set const &	changed_visibility_set,
										t_tile_point_set const &	changed_shroud_set );
	void							record_animating_object_ids_in_rect(
										t_screen_rect const &	rect );
	bool							record_if_animating( t_object_id object_id );
	void							reevaluate_animating_object_ids();
	void							reevaluate_animating_object_ids(
										t_object_height_delta_set const &	changed_object_height_deltas );
	void							refresh_terrain( t_clip_list const & clip_list );
	void							refresh_terrain(
										t_tile_point_set const &	tile_points,
										t_clip_list &				result );
	void							remove_animating_object_id(
										t_object_id object_id );
	t_object_id_set::iterator		remove_animating_object_id(
										t_object_id_set::iterator iter );
	void							update_shroud_mask( t_screen_rect const & clip_rect );
	void							update_terrain_buffer( t_screen_rect const & clip_rect );
};

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer::t_impl::t_draw_terrain_map_adapter class

class t_map_renderer::t_impl::t_draw_terrain_map_adapter
{
public:
	// Constructor
	explicit t_draw_terrain_map_adapter( t_map_renderer::t_impl & map_renderer_impl );

	// Member functions
	t_metatile_map const &						get_metatile_map() const;
	t_screen_point								get_screen_point( t_map_point_2d const & point ) const;
	int											get_size() const;
	t_abstract_tile const &						get_tile( t_level_map_point_2d const & point ) const;
	t_quad< t_abstract_tile_vertex const & >	get_tile_vertex_quad( t_level_map_point_2d const & point ) const;
	t_screen_point								get_view_size() const;
	bool										is_valid( t_map_point_2d const & point ) const;

private:
	// Data mebers
	t_map_renderer::t_impl &	m_map_renderer_impl;
};

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer::t_impl::t_draw_terrain_map_adapter members

/////////////////////////////////////////////////////////////////////////////
inline t_map_renderer::t_impl::t_draw_terrain_map_adapter::t_draw_terrain_map_adapter(
	t_map_renderer::t_impl & map_renderer_impl )
	:	m_map_renderer_impl( map_renderer_impl )
{
}

/////////////////////////////////////////////////////////////////////////////
inline t_metatile_map const &
t_map_renderer::t_impl::t_draw_terrain_map_adapter::get_metatile_map() const
{
	return m_map_renderer_impl.get_internal_map_data().get_metatile_map();
}

/////////////////////////////////////////////////////////////////////////////
inline t_screen_point
t_map_renderer::t_impl::t_draw_terrain_map_adapter::get_screen_point(
	t_map_point_2d const & point ) const
{
	return m_map_renderer_impl.m_adventure_map.get_screen_point( point );
}

/////////////////////////////////////////////////////////////////////////////
inline int t_map_renderer::t_impl::t_draw_terrain_map_adapter::get_size() const
{
	return m_map_renderer_impl.m_adventure_map.get_size();
}

/////////////////////////////////////////////////////////////////////////////
inline t_abstract_tile const &
t_map_renderer::t_impl::t_draw_terrain_map_adapter::get_tile(
	t_level_map_point_2d const & point ) const
{
	return m_map_renderer_impl.m_adventure_map.get_tile( point );
}

/////////////////////////////////////////////////////////////////////////////
inline t_quad< t_abstract_tile_vertex const & >
t_map_renderer::t_impl::t_draw_terrain_map_adapter::get_tile_vertex_quad(
	t_level_map_point_2d const & point ) const
{
	return m_map_renderer_impl.m_adventure_map.get_tile_vertex_quad( point );
}

/////////////////////////////////////////////////////////////////////////////
inline t_screen_point
t_map_renderer::t_impl::t_draw_terrain_map_adapter::get_view_size() const
{
	return m_map_renderer_impl.m_adventure_map.get_view_size();
}

/////////////////////////////////////////////////////////////////////////////
inline bool
t_map_renderer::t_impl::t_draw_terrain_map_adapter::is_valid(
	t_map_point_2d const & point ) const
{
	return m_map_renderer_impl.m_adventure_map.is_valid( point );
}

/////////////////////////////////////////////////////////////////////////////
class t_map_renderer::t_impl::t_draw_tile_shroud_mask_func
{
public:
	// Constructor
	t_draw_tile_shroud_mask_func(
		t_abstract_bitmap8 &	shroud_mask,
		t_adv_map const &		adv_map,
		int						team_view );

	// Operator
	void	operator()(
				t_draw_terrain_map_adapter		map_adapter,
				t_level_map_point_2d const &	tile_point,
				t_screen_point const &			screen_pos,
				t_screen_rect const &			clip_rect ) const;

private:
	// Data members
	t_abstract_bitmap8 &	m_shroud_mask;
	t_adv_map const &		m_adv_map;
	int						m_team_view;
};

/////////////////////////////////////////////////////////////////////////////
inline t_map_renderer::t_impl::t_draw_tile_shroud_mask_func::t_draw_tile_shroud_mask_func(
	t_abstract_bitmap8 &	shroud_mask,
	t_adv_map const &		adv_map,
	int						team_view ) 
	:	m_shroud_mask( shroud_mask ),
		m_adv_map( adv_map ),
		m_team_view( team_view )
{
	assert( m_team_view >= 0 );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::t_draw_tile_shroud_mask_func::operator()(
	t_draw_terrain_map_adapter		map_adapter,
	t_level_map_point_2d const &	tile_point,
	t_screen_point const &			screen_pos,
	t_screen_rect const &			clip_rect ) const
{
	draw_tile_shroud_mask(
		m_adv_map,
		m_team_view,
		tile_point,
		m_shroud_mask,
		screen_pos,
		clip_rect );
}

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer::t_impl::t_draw_tile_passability_func class

class t_map_renderer::t_impl::t_draw_tile_passability_func
{
public:
	// Constructor
	t_draw_tile_passability_func(
		t_adv_map const &		adventure_map,
		t_abstract_bitmap16 &	dest );

	// Operator
	void	operator()(
				t_draw_terrain_map_adapter		draw_terrain_map_adapter,
				t_level_map_point_2d const &	tile_point,
				t_screen_point const &			screen_pos,
				t_screen_rect const &			clip_rect ) const;

private:
	// Data members
	t_adv_map const &		m_adventure_map;
	t_abstract_bitmap16 &	m_dest;
};

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer::t_impl::t_draw_tile_passability_func members

/////////////////////////////////////////////////////////////////////////////
inline
t_map_renderer::t_impl::t_draw_tile_passability_func::t_draw_tile_passability_func(
	t_adv_map const &		adventure_map,
	t_abstract_bitmap16 &	dest )
	:	m_adventure_map( adventure_map ),
		m_dest( dest )
{
}

/////////////////////////////////////////////////////////////////////////////
inline void
t_map_renderer::t_impl::t_draw_tile_passability_func::operator()(
	t_draw_terrain_map_adapter		draw_terrain_map_adapter,
	t_level_map_point_2d const &	tile_point,
	t_screen_point const &			screen_pos,
	t_screen_rect const &			clip_rect ) const
{
	draw_tile_passability(
		m_adventure_map,
		tile_point,
		m_dest,
		screen_pos,
		clip_rect );
}

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer::t_impl members

/////////////////////////////////////////////////////////////////////////////
inline t_internal_map_data & t_map_renderer::t_impl::get_internal_map_data() const
{
	return *m_internal_map_data_iter->second;
}

/////////////////////////////////////////////////////////////////////////////
inline void	t_map_renderer::t_impl::enable_animation( bool arg )
{
	if (arg == m_animation_enabled)
		return;
	m_animation_enabled = arg;
	if (arg)
	{
		if (!is_active() && !m_animating_object_ids.empty())
			resume_idle_processing();
	}
	else
	{
		if (is_active())
			suspend_idle_processing();
	}
}

/////////////////////////////////////////////////////////////////////////////
inline bool t_map_renderer::t_impl::is_under_fog_of_war(
	t_object_id	object_id ) const
{
	return ::is_under_fog_of_war( m_adventure_map, m_team_view, object_id );
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_impl::t_impl(
	t_map_renderer &	outer,
	t_client *			client_ptr,
	t_adv_map const &	adventure_map )
	:	t_idle_processor( 1 ),
		m_outer( outer ),
		m_client_ptr( client_ptr ),
		m_adventure_map( adventure_map ),
		m_last_update_time( get_time() ),
		m_view_grid( false ),
		m_view_level( 0 ),
		m_view_pos( 0, -k_max_tile_vertex_height ),
		m_view_size( 0, 0 ),
		m_team_view( -1 ),
		m_view_passability( false ),
		m_dirty_quadtree()
{
	// Get the internal map data map
	m_internal_map_list = get_internal_map_list();

	// Lookup this adventure map in the internal map
	t_internal_map_data_map::iterator internal_map_data_iter = m_internal_map_list->find( &adventure_map );
	if ( internal_map_data_iter != m_internal_map_list->end() )
	{
		// There is already an entry for this map so let's use it
		m_internal_map_data_iter = internal_map_data_iter;
		m_internal_map_data_iter->second->add_map_renderer( &m_outer );
	}
	else
	{
		// We need to create an entry for this map
		t_internal_data_ptr new_internal_map_data_ptr 
			= new t_internal_map_data( adventure_map );

		m_internal_map_data_iter 
			= m_internal_map_list->insert( std::make_pair( &adventure_map, 
			                               new_internal_map_data_ptr ) ).first;
		assert( m_internal_map_list->find( &adventure_map ) == m_internal_map_data_iter );

		try
		{
			m_internal_map_data_iter->second->add_map_renderer( &m_outer );
		}
		catch ( ... )
		{
			m_internal_map_list->erase( m_internal_map_data_iter );
			throw;
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_impl::~t_impl()
{
	// Remove ourself from the internal map data
	int count = m_internal_map_data_iter->second->remove_map_renderer( &m_outer );

	// If this was the last map renderer for this particular map, delete the internal map data
	if ( count <= 0 )
	{
		assert( m_internal_map_list->find( m_internal_map_data_iter->first ) 
			    == m_internal_map_data_iter );
		 
		m_internal_map_list->erase( m_internal_map_data_iter );
	}
}

/////////////////////////////////////////////////////////////////////////////
bool t_map_renderer::t_impl::add_animating_object_id(
	t_object_id	object_id )
{
	bool was_empty = m_animating_object_ids.empty();
	bool result = m_animating_object_ids.insert( object_id ).second;
	if ( was_empty && m_animation_enabled && !is_active())
		resume_idle_processing();
	return result;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::adjust_object_animation_for_shroud_change(
	t_tile_point_set const &	tile_points,
	t_clip_list &				clip_list )
{
	// Run through the objects over the affected cells
	t_object_id_set potentially_visible_object_ids;
	t_object_id_set potentially_hidden_object_ids;

	t_tile_point_set::const_iterator tile_point_end = tile_points.end();
	t_tile_point_set::const_iterator tile_point_iter = tile_points.begin();
	for ( ; tile_point_iter != tile_point_end; ++tile_point_iter )
	{
		t_adv_map::t_tile const & tile =
			m_adventure_map.get_tile( t_level_map_point_2d( *tile_point_iter, m_view_level ) );
		
		t_object_id_set & object_ids =
			tile.get_visibility( m_team_view ) == k_tile_visible
				? potentially_visible_object_ids
				: potentially_hidden_object_ids;

		int count = tile.get_intersecting_object_count();
		int i;
		for ( i = 0; i < count; ++i )
		{
			t_object_id object_id = tile.get_intersecting_object_id( i );
			t_object const & object = m_adventure_map.get_object( object_id );
			if ( object.animates() )
				object_ids.insert( object_id );
		}
	}

	// Stop animating any objects which are newly 'fogged'
	t_object_id_set::const_iterator object_id_end = potentially_hidden_object_ids.end();
	t_object_id_set::const_iterator object_id_iter = potentially_hidden_object_ids.begin();
	for ( ; object_id_iter != object_id_end; ++object_id_iter )
	{
		t_object_id object_id = *object_id_iter;
		if (	is_under_fog_of_war( object_id )
			&&	m_animating_object_ids.find( object_id ) != m_animating_object_ids.end() )
		{
			invalidate_object( object_id, m_last_update_time, clip_list );
			invalidate_object( object_id, 0, clip_list );
			remove_animating_object_id( object_id );
		}
	}

	// Start animating any objects which are newly 'fogged'
	object_id_end = potentially_visible_object_ids.end();
	object_id_iter = potentially_visible_object_ids.begin();
	for ( ; object_id_iter != object_id_end; ++object_id_iter )
	{
		t_object_id object_id = *object_id_iter;
		if ( record_if_animating( object_id ) )
		{
			invalidate_object( object_id, m_last_update_time, clip_list );
			invalidate_object( object_id, 0, clip_list );
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::adjust_object_visibility_for_visibility_change(
	t_visibility_set const &	tile_points,
	t_clip_list &				clip_list )
{
	// Run through the objects over the affected cells
	t_object_id_set potentially_visible_object_ids;
	t_object_id_set potentially_hidden_object_ids;

	t_visibility_set::const_iterator tile_point_end = tile_points.end();
	t_visibility_set::const_iterator tile_point_iter = tile_points.begin();
	for ( ; tile_point_iter != tile_point_end; ++tile_point_iter )
	{
		t_adv_map::t_tile const & tile =
			m_adventure_map.get_tile( t_level_map_point_2d( *tile_point_iter, m_view_level ) );
		t_tile_visibility tile_visibility = tile.get_visibility( m_team_view );

		bool visibility_increased =
				tile_visibility > tile_point_iter->get_visibility()
			||	(	tile_visibility == tile_point_iter->get_visibility()
				&&	tile.get_anti_stealth( m_team_view ) > tile_point_iter->get_anti_stealth() );
		
		t_object_id_set & object_ids =
			visibility_increased ? potentially_visible_object_ids : potentially_hidden_object_ids;

		int count = tile.get_intersecting_object_count();
		int i;
		for ( i = 0; i < count; ++i )
		{
			t_object_id object_id = tile.get_intersecting_object_id( i );
			t_object const & object = m_adventure_map.get_object( object_id );

			if ( object.can_be_hidden() )
				object_ids.insert( object_id );
		}
	}

	// Invalidate objects which are newly hidden
	t_object_id_set::const_iterator object_id_end = potentially_hidden_object_ids.end();
	t_object_id_set::const_iterator object_id_iter = potentially_hidden_object_ids.begin();

	for ( ; object_id_iter != object_id_end; ++object_id_iter )
	{
		t_object_id object_id = *object_id_iter;
		t_object const & object = m_adventure_map.get_object( object_id );

		if ( !object.is_visible_to( m_team_view ) )
		{
			invalidate_object( object_id, get_last_update_time( object_id, object ), clip_list );
			if ( object.animates() )
				remove_animating_object_id( object_id );
		}
	}

	// Invalidate any objects which are newly visible
	object_id_end = potentially_visible_object_ids.end();
	object_id_iter = potentially_visible_object_ids.begin();
	for ( ; object_id_iter != object_id_end; ++object_id_iter )
	{
		t_object_id object_id = *object_id_iter;
		t_object const & object = m_adventure_map.get_object( object_id );

		if ( object.is_visible_to( m_team_view ) )
		{
			record_if_animating( object_id );
			invalidate_object( object_id, get_last_update_time( object_id, object ), clip_list );
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
t_clip_list t_map_renderer::t_impl::build_clip_list(
	t_tile_point_set const &	tile_points ) const
{
	// Build a clip list of all of the extents of the tiles points specified
	t_screen_rect clip_rect( 0, 0, m_view_size.x, m_view_size.y );
	t_clip_list result;

	t_tile_point_set::const_iterator tile_point_end = tile_points.end();
	t_tile_point_set::const_iterator tile_point_iter = tile_points.begin();
	for ( ; tile_point_iter != tile_point_end; ++tile_point_iter )
	{
		t_map_point_2d tile_point = *tile_point_iter;

		// Compute the extent of this tile
		t_screen_point tile_screen_point = m_adventure_map.get_screen_point( tile_point );
		tile_screen_point -= m_view_pos;
		tile_screen_point.x -= k_adventure_tile_width / 2;

		t_abstract_adventure_map::t_const_tile_vertex_quad tile_vertex_quad =
			m_adventure_map.get_tile_vertex_quad( t_level_map_point_2d( tile_point, m_view_level ) );
		int tile_left_y = tile_screen_point.y + k_adventure_tile_height / 2 - tile_vertex_quad.left.get_height();
		int tile_top_y = tile_screen_point.y - tile_vertex_quad.top.get_height();
		int tile_right_y = tile_screen_point.y + k_adventure_tile_height / 2 - tile_vertex_quad.right.get_height();
		int tile_bottom_y = tile_screen_point.y + k_adventure_tile_height - tile_vertex_quad.bottom.get_height();

		if ( tile_left_y < tile_top_y )
			tile_top_y = tile_left_y;
		else if ( tile_left_y > tile_bottom_y )
			tile_bottom_y = tile_left_y;

		if ( tile_right_y < tile_top_y )
			tile_top_y = tile_right_y;
		else if ( tile_right_y > tile_bottom_y )
			tile_bottom_y = tile_right_y;

		t_screen_rect	tile_screen_rect(
							tile_screen_point.x,
							tile_top_y,
							tile_screen_point.x + k_adventure_tile_width,
							tile_bottom_y );

		// Add it to the result
		if ( intersect( tile_screen_rect, clip_rect ) )
			result += intersection( tile_screen_rect, clip_rect );
	}

	return result;
}

/////////////////////////////////////////////////////////////////////////////
t_clip_list t_map_renderer::t_impl::build_clip_list_for_shroud(
	t_tile_point_set const &	tile_points ) const
{
	// Build a clip list of all of the extents of the tiles points specified
	t_screen_rect clip_rect( 0, 0, m_view_size.x, m_view_size.y );
	t_clip_list result;

	t_tile_point_set::const_iterator tile_point_end = tile_points.end();
	t_tile_point_set::const_iterator tile_point_iter = tile_points.begin();
	for ( ; tile_point_iter != tile_point_end; ++tile_point_iter )
	{
		t_map_point_2d tile_point = *tile_point_iter;

		// Compute the extent of this tile
		t_screen_point tile_screen_point = m_adventure_map.get_screen_point( tile_point );
		tile_screen_point -= m_view_pos;
		tile_screen_point.x -= k_adventure_tile_width / 2;

		t_abstract_adventure_map::t_const_tile_vertex_quad tile_vertex_quad =
			m_adventure_map.get_tile_vertex_quad( t_level_map_point_2d( tile_point, m_view_level ) );
		int tile_left_y = tile_screen_point.y + k_adventure_tile_height / 2 - tile_vertex_quad.left.get_height();
		int tile_top_y = tile_screen_point.y - tile_vertex_quad.top.get_height();
		int tile_right_y = tile_screen_point.y + k_adventure_tile_height / 2 - tile_vertex_quad.right.get_height();
		int tile_bottom_y = tile_screen_point.y + k_adventure_tile_height - tile_vertex_quad.bottom.get_height();

		if ( tile_left_y < tile_top_y )
			tile_top_y = tile_left_y;
		else if ( tile_left_y > tile_bottom_y )
			tile_bottom_y = tile_left_y;

		if ( tile_right_y < tile_top_y )
			tile_top_y = tile_right_y;
		else if ( tile_right_y > tile_bottom_y )
			tile_bottom_y = tile_right_y;

		t_map_margin_type margin_type = compute_tile_point_margin_type( m_adventure_map, tile_point );
		if ( margin_type == k_map_margin_top )
			tile_top_y = -k_max_tile_vertex_height - m_view_pos.y;
		else if ( margin_type == k_map_margin_bottom )
			tile_bottom_y = m_adventure_map.get_view_size().y - m_view_pos.y;

		t_screen_rect	tile_screen_rect(
							tile_screen_point.x,
							tile_top_y,
							tile_screen_point.x + k_adventure_tile_width,
							tile_bottom_y );

		// Add it to the result
		if ( intersect( tile_screen_rect, clip_rect ) )
			result += intersection( tile_screen_rect, clip_rect );
	}

	return result;
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_tile_point_set
t_map_renderer::t_impl::build_map_rect_point_set(
	t_map_point_2d const &	pos,
	t_map_point_2d const &	size ) const
{
	int map_size = m_adventure_map.get_size();

	int begin_row = pos.row;
	if ( begin_row < 0 )
		begin_row = 0;

	int end_row = pos.row + size.row;
	if ( end_row > map_size )
		end_row = map_size;

	t_tile_point_set result;

	t_map_point_2d tile_point;
	for ( tile_point.row = begin_row; tile_point.row < end_row; ++tile_point.row )
	{
		int row_start = m_adventure_map.get_row_start( tile_point.row );
		int begin_col = pos.column;
		if ( begin_col < row_start )
			begin_col = row_start;

		int row_end = m_adventure_map.get_row_end( tile_point.row );
		int end_col = pos.column + size.column;
		if ( end_col > row_end )
			end_col = row_end;
		
		for ( tile_point.column = begin_col; tile_point.column < end_col; ++tile_point.column )
			result.insert( tile_point );
	}

	return result;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::clear_animating_object_ids()
{
	if ( !m_animating_object_ids.empty() )
	{
		m_animating_object_ids.clear();
		if (is_active())
			suspend_idle_processing();
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::dirty( t_screen_rect const & rect )
{
	//m_dirty_list += rect;
	m_dirty_quadtree.insert ( rect , false );

	static std::vector< t_screen_rect > dirtied_rects( 1 );
	dirtied_rects.front() = rect;

	m_outer.on_rects_dirtied( dirtied_rects );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::dirty( t_clip_list const & clip_list )
{
	//m_dirty_list += clip_list;

	t_clip_list::t_const_iterator iter = clip_list.begin();

	while(iter != clip_list.end())
	{
		m_dirty_quadtree.insert ( *iter , false );
		iter++;
	}

	static std::vector< t_screen_rect > dirtied_rects;
	std::copy( clip_list.begin(), clip_list.end(), std::back_inserter( dirtied_rects ) );

	try
	{
		m_outer.on_rects_dirtied( dirtied_rects );
	}
	catch ( ... )
	{
		dirtied_rects.clear();
		throw;
	}

	dirtied_rects.clear();
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_bottom_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.bottom > map_view_rect.bottom );
	assert( clip_rect.width() > 0 );

	t_screen_rect edge_rect = clip_rect;
	if ( edge_rect.top < map_view_rect.bottom )
		edge_rect.top = map_view_rect.bottom;

	if ( edge_rect.left < map_view_rect.left )
	{
		draw_bottom_left_edge( edge_rect, map_view_rect );
		edge_rect.left = map_view_rect.left;
		if ( edge_rect.width() <= 0 )
			return;
	}

	if ( edge_rect.right > map_view_rect.right )
	{
		draw_bottom_right_edge( edge_rect, map_view_rect );
		edge_rect.right = map_view_rect.right;
		if ( edge_rect.width() <= 0 )
			return;
	}

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_bottom();

	int frame_height = frame_layer.get_height();
	if ( edge_rect.bottom > map_view_rect.bottom + frame_height )
	{
		t_screen_rect fill_rect = edge_rect;
		if ( fill_rect.top < map_view_rect.bottom + frame_height )
			fill_rect.top = map_view_rect.bottom + frame_height;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		edge_rect.bottom = fill_rect.top;
		if ( edge_rect.height() < 0 )
			return;
	}
	assert( edge_rect.height() <= frame_height );

	int frame_width = frame_layer.get_width();

	int frame_x = map_view_rect.left + ( ( edge_rect.left - map_view_rect.left ) / frame_width ) * frame_width;
	assert( frame_x + frame_width > edge_rect.left );

	t_screen_rect dest_rect( edge_rect.left, edge_rect.top, frame_x + frame_width, edge_rect.bottom );
	int source_y = edge_rect.top - map_view_rect.bottom;
	while ( true )
	{
		bool last;
		if ( dest_rect.right >= edge_rect.right )
		{
			dest_rect.right = edge_rect.right;
			last = true;
		}
		else
			last = false;

		frame_layer.draw_to(
			t_screen_rect( t_screen_point( dest_rect.left - frame_x, source_y ), dest_rect.size() ),
			*m_back_buffer_ptr,
			dest_rect.top_left() );

		if ( last )
			break;

		dest_rect.left = frame_x = dest_rect.right;
		dest_rect.right += frame_width;
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_bottom_left_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.left < map_view_rect.left );
	assert( clip_rect.top >= map_view_rect.bottom );

	t_screen_rect corner_rect = clip_rect;
	if ( corner_rect.right > map_view_rect.left )
		corner_rect.right = map_view_rect.left;

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_bottom_left();

	int frame_height = frame_layer.get_height();
	if ( corner_rect.bottom > map_view_rect.bottom + frame_height )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.top < map_view_rect.bottom + frame_height )
			fill_rect.top = map_view_rect.bottom + frame_height;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.bottom = fill_rect.top;
		if ( corner_rect.height() <= 0 )
			return;
	}

	int frame_width = frame_layer.get_width();
	if ( corner_rect.left < map_view_rect.left - frame_width )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.right > map_view_rect.left - frame_width )
			fill_rect.right = map_view_rect.left - frame_width;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.left = fill_rect.right;
		if ( corner_rect.width() <= 0 )
			return;
	}

	assert( corner_rect.width() <= frame_width );
	assert( corner_rect.height() <= frame_height );

	t_screen_rect source_rect = corner_rect;
	source_rect -= map_view_rect.bottom_left() - t_screen_point( frame_width, 0 );
	frame_layer.draw_to( source_rect, *m_back_buffer_ptr, corner_rect.top_left() );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_bottom_margin_column(
	t_screen_rect const &	clip_rect,
	int						margin_column_num )
{
	t_internal_map_data & internal_map_data = get_internal_map_data();

	int left_height = internal_map_data.get_bottom_margin_point_height( m_view_level, margin_column_num );
	int right_height = internal_map_data.get_bottom_margin_point_height( m_view_level, margin_column_num + 1 );

	int view_bottom = m_adventure_map.get_view_size().y;

	t_screen_rect column_rect;
	column_rect.left = margin_column_num * k_margin_column_width;
	column_rect.right = column_rect.left + k_margin_column_width;
	column_rect.bottom = view_bottom;
	column_rect.top = column_rect.bottom - ( left_height < right_height ? left_height : right_height );
	column_rect -= m_view_pos;

	t_bitmap_layer const & texture = m_margin_texture_layers.get_bottom();

	if ( intersect( column_rect, clip_rect ) )
	{
		t_screen_rect fill_rect = intersection( column_rect, clip_rect );
		t_screen_point source_point = fill_rect.top_left() + m_view_pos;
		wrap_draw( *m_back_buffer_ptr, fill_rect.top_left(), texture, source_point, fill_rect.size() );
	}

	if ( left_height == right_height )
		return;

	column_rect.bottom = column_rect.top;
	if ( left_height < right_height )
		column_rect.top -= right_height - left_height;
	else
		column_rect.top -= left_height - right_height;

	t_screen_point source_point = column_rect.top_left() + m_view_pos;

	if ( left_height < right_height )
		wrap_draw_bottom_right( *m_back_buffer_ptr, column_rect, texture, source_point, clip_rect );
	else
		wrap_draw_bottom_left( *m_back_buffer_ptr, column_rect, texture, source_point, clip_rect );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_bottom_margin_column_shroud_mask(
	int						margin_column_num,
	t_screen_rect const &	rect,
	t_screen_point const &	view_pos )
{
	assert( m_team_view >= 0 );

	t_internal_map_data & internal_map_data = get_internal_map_data();

	int left_height = internal_map_data.get_bottom_margin_point_height( m_view_level, margin_column_num );
	int right_height = internal_map_data.get_bottom_margin_point_height( m_view_level, margin_column_num + 1 );

	int view_bottom = m_adventure_map.get_view_size().y;

	t_screen_rect column_rect;
	column_rect.left = margin_column_num * k_margin_column_width;
	column_rect.right = column_rect.left + k_margin_column_width;
	column_rect.bottom = view_bottom;
	column_rect.top = column_rect.bottom - ( left_height < right_height ? right_height : left_height );
	column_rect -= view_pos;

	if ( !intersect( column_rect, rect ) )
		return;

	t_level_map_point_2d	tile_point(
								compute_bottom_margin_column_tile_point(
									m_adventure_map,
									margin_column_num ),
								m_view_level );

	bool left_half = ( margin_column_num & 1 ) == 0;

	t_bitmap_1d< t_uint8 > & column_shroud_mask = get_margin_column_shroud_mask();

	draw_tile_half_horizontal_shroud_mask(
		m_adventure_map,
		m_team_view,
		tile_point,
		left_half,
		column_shroud_mask );

	column_rect.top += left_height < right_height ? right_height - left_height : left_height - right_height;

	if ( intersect( column_rect, rect ) )
	{
		t_screen_rect clipped_column_rect = intersection( column_rect, rect );
		bit_blt_horizontal_source(
			*m_shroud_mask_ptr,
			clipped_column_rect.top_left(),
			column_shroud_mask,
			clipped_column_rect.left - column_rect.left,
			clipped_column_rect.size() );
	}

	column_rect.bottom = column_rect.top;
	if ( left_height < right_height )
	{
		column_rect.top = column_rect.bottom - ( right_height - left_height );
		bit_blt_horizontal_source_bottom_right(
			*m_shroud_mask_ptr,
			column_rect,
			column_shroud_mask,
			0,
			rect );
	}
	else if ( left_height > right_height )
	{
		column_rect.top = column_rect.bottom - ( left_height - right_height );
		bit_blt_horizontal_source_bottom_left(
			*m_shroud_mask_ptr,
			column_rect,
			column_shroud_mask,
			0,
			rect );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_bottom_right_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.right > map_view_rect.right );
	assert( clip_rect.top >= map_view_rect.bottom );

	t_screen_rect corner_rect = clip_rect;
	if ( corner_rect.left < map_view_rect.right )
		corner_rect.left = map_view_rect.right;

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_bottom_right();

	int frame_height = frame_layer.get_height();
	if ( corner_rect.bottom > map_view_rect.bottom + frame_height )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.top < map_view_rect.bottom + frame_height )
			fill_rect.top = map_view_rect.bottom + frame_height;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.bottom = fill_rect.top;
		if ( corner_rect.height() <= 0 )
			return;
	}

	int frame_width = frame_layer.get_width();
	if ( corner_rect.right > map_view_rect.right + frame_width )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.left < map_view_rect.right + frame_width )
			fill_rect.left = map_view_rect.right + frame_width;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.right = fill_rect.left;
		if ( corner_rect.width() <= 0 )
			return;
	}

	assert( corner_rect.width() <= frame_width );
	assert( corner_rect.height() <= frame_height );

	t_screen_rect source_rect = corner_rect - map_view_rect.bottom_right();
	frame_layer.draw_to( source_rect, *m_back_buffer_ptr, corner_rect.top_left() );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_edges_and_adjust_rect(
	t_screen_rect &	clip_rect )
{
	t_screen_point map_view_size = m_adventure_map.get_view_size();
	t_screen_rect map_view_rect( 0, -k_max_tile_vertex_height, map_view_size.x, map_view_size.y );
	map_view_rect -= m_view_pos;

	if ( clip_rect.top < map_view_rect.top )
	{
		draw_top_edge( clip_rect, map_view_rect );
		clip_rect.top = map_view_rect.top;
		if ( clip_rect.height() <= 0 )
			return;
	}

	if ( clip_rect.bottom > map_view_rect.bottom )
	{
		draw_bottom_edge( clip_rect, map_view_rect );
		clip_rect.bottom = map_view_rect.bottom;
		if ( clip_rect.height() <= 0 )
			return;
	}

	if ( clip_rect.left < map_view_rect.left )
	{
		draw_left_edge( clip_rect, map_view_rect );
		clip_rect.left = map_view_rect.left;
		if ( clip_rect.width() <= 0 )
			return;
	}

	if ( clip_rect.right > map_view_rect.right )
	{
		draw_right_edge( clip_rect, map_view_rect );
		clip_rect.right = map_view_rect.right;
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_left_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.top >= map_view_rect.top );
	assert( clip_rect.bottom <= map_view_rect.bottom );

	t_screen_rect edge_rect = clip_rect;
	if ( edge_rect.right > map_view_rect.left )
		edge_rect.right = map_view_rect.left;

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_left();

	int frame_width = frame_layer.get_width();
	if ( edge_rect.left < map_view_rect.left - frame_width )
	{
		t_screen_rect fill_rect = edge_rect;
		if ( fill_rect.right > map_view_rect.left - frame_width )
			fill_rect.right = map_view_rect.left - frame_width;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		edge_rect.left = fill_rect.right;
		if ( edge_rect.width() < 0 )
			return;
	}
	assert( edge_rect.width() <= frame_width );

	int frame_height = frame_layer.get_height();

	int frame_y = map_view_rect.top + ( ( edge_rect.top - map_view_rect.top ) / frame_height ) * frame_height;
	assert( frame_y + frame_height > edge_rect.top );

	t_screen_rect dest_rect( edge_rect.left, edge_rect.top, edge_rect.right, frame_y + frame_height );
	int source_x = edge_rect.left - ( map_view_rect.left - frame_width );
	while ( true )
	{
		bool last;
		if ( dest_rect.bottom >= edge_rect.bottom )
		{
			dest_rect.bottom = edge_rect.bottom;
			last = true;
		}
		else
			last = false;

		frame_layer.draw_to(
			t_screen_rect( t_screen_point( source_x, dest_rect.top - frame_y ), dest_rect.size() ),
			*m_back_buffer_ptr,
			dest_rect.top_left() );

		if ( last )
			break;

		dest_rect.top = frame_y = dest_rect.bottom;
		dest_rect.bottom += frame_height;
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_map( t_screen_rect const & clip_rect )
{
	assert( m_back_buffer_ptr.get() != 0 );
	assert( is_normalized( clip_rect ) );

	if ( clip_rect.width() <= 0 || clip_rect.height() <= 0 )
		return;

	t_screen_rect adjusted_clip_rect = clip_rect;

	draw_edges_and_adjust_rect( adjusted_clip_rect );
	if ( adjusted_clip_rect.width() <= 0 || adjusted_clip_rect.height() <= 0 )
		return;

	if ( m_team_view != k_player_gray )
	{
		draw_terrain( adjusted_clip_rect );
		draw_map_margins( adjusted_clip_rect );
		draw_objects( adjusted_clip_rect );
	}

	draw_shroud( adjusted_clip_rect );

	if ( m_view_passability )
		draw_passability( adjusted_clip_rect );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_map_margins( t_screen_rect const & clip_rect )
{
	// Draw the top margin of the map
	if ( clip_rect.top + m_view_pos.y < 0 )
	{
		int margin_column_start = ( clip_rect.left + m_view_pos.x ) / k_margin_column_width;
		int margin_column_end = ( ( clip_rect.right + m_view_pos.x ) + k_margin_column_width - 1 ) / k_margin_column_width;

		int margin_column_num;
		for ( margin_column_num = margin_column_start; margin_column_num < margin_column_end; ++margin_column_num )
			draw_top_margin_column( clip_rect, margin_column_num );
	}

	// Draw the bottom margin of the map
	if ( clip_rect.bottom + m_view_pos.y > m_adventure_map.get_view_size().y - k_max_tile_vertex_height )
	{
		int margin_column_start = ( clip_rect.left + m_view_pos.x ) / k_margin_column_width;
		int margin_column_end = ( ( clip_rect.right + m_view_pos.x ) + k_margin_column_width - 1 ) / k_margin_column_width;

		int margin_column_num;
		for ( margin_column_num = margin_column_start; margin_column_num < margin_column_end; ++margin_column_num )
			draw_bottom_margin_column( clip_rect, margin_column_num );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_objects( t_screen_rect const & clip_rect )
{
	// Compute the rect of view cells to draw
	t_screen_rect	view_cell_rect(
						( clip_rect.left + m_view_pos.x ) >> k_adventure_map_view_cell_shift,
						( clip_rect.top + m_view_pos.y ) >> k_adventure_map_view_cell_shift,
						( clip_rect.right + m_view_pos.x + k_adventure_map_view_cell_size - 1 ) >> k_adventure_map_view_cell_shift,
						( clip_rect.bottom + m_view_pos.y + k_adventure_map_view_cell_size - 1 ) >> k_adventure_map_view_cell_shift );

	// Draw objects cell by cell
	t_screen_point view_cell_point;
	for (	view_cell_point.y = view_cell_rect.top;
			view_cell_point.y < view_cell_rect.bottom;
			++view_cell_point.y )
	{
		for (	view_cell_point.x = view_cell_rect.left;
				view_cell_point.x < view_cell_rect.right;
				++view_cell_point.x )
		{
			t_screen_point	cell_screen_point(
								view_cell_point.x << k_adventure_map_view_cell_shift,
								view_cell_point.y << k_adventure_map_view_cell_shift );
			cell_screen_point -= m_view_pos;
			t_screen_rect	cell_screen_rect(
								cell_screen_point,
								t_screen_point(
									k_adventure_map_view_cell_size,
									k_adventure_map_view_cell_size ) );
			cell_screen_rect = intersection( cell_screen_rect, clip_rect );

			typedef t_adv_map::t_view_cell t_view_cell;
			t_view_cell const & view_cell = m_adventure_map.get_view_cell( m_view_level, view_cell_point );

			// Draw underlays
			t_view_cell::t_const_object_subimage_info_iterator subimage_info_iter = view_cell.object_subimage_begin();
			for ( ; subimage_info_iter != view_cell.object_subimage_end(); ++subimage_info_iter )
			{
				t_object_id object_id = subimage_info_iter->object_id;
				t_object const & object = m_adventure_map.get_object( object_id );
				if ( !object.subimage_is_underlay( subimage_info_iter->subimage_num ) )
					break;

				if ( !object.is_visible_to( m_team_view ))
					continue;

				t_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
				t_screen_point obj_screen_point = m_adventure_map.get_screen_point( object_pos ) - m_view_pos;
				obj_screen_point.y -= m_adventure_map.get_object_height( object_id );
				t_screen_rect rel_cell_rect = cell_screen_rect - obj_screen_point;
				t_uint32 last_update_time = get_last_update_time( object_id, object );
				t_screen_rect image_rect = object.get_rect( last_update_time );
				if ( intersect( rel_cell_rect, image_rect ) )
				{
					object.draw_subimage_to(
						subimage_info_iter->subimage_num,
						last_update_time,
						intersection( rel_cell_rect, image_rect ),
						*m_back_buffer_ptr,
						obj_screen_point );
				}
			}

			// Draw shadows
			t_view_cell::t_const_shadow_object_id_iterator shadow_id_iter = view_cell.shadow_begin();
			for ( ; shadow_id_iter != view_cell.shadow_end(); ++shadow_id_iter )
			{
				t_object const & object = m_adventure_map.get_object( *shadow_id_iter );

				if ( !object.is_visible_to( m_team_view ))
					continue;

				t_map_point_2d object_pos = m_adventure_map.get_object_pos( *shadow_id_iter );
				t_screen_point obj_screen_point = m_adventure_map.get_screen_point( object_pos ) - m_view_pos;
				obj_screen_point.y -= m_adventure_map.get_object_height( *shadow_id_iter );
				t_screen_rect rel_cell_rect = cell_screen_rect - obj_screen_point;
				t_uint32 last_update_time = get_last_update_time( *shadow_id_iter, object );
				t_screen_rect image_rect = object.get_shadow_rect( last_update_time );
				if ( intersect( rel_cell_rect, image_rect ) )
				{
					object.draw_shadow_to(
						last_update_time,
						intersection( rel_cell_rect, image_rect ),
						*m_back_buffer_ptr,
						obj_screen_point );
				}
			}

			// Draw overlays
			for ( ; subimage_info_iter != view_cell.object_subimage_end(); ++subimage_info_iter )
			{
				t_object_id object_id = subimage_info_iter->object_id;
				t_object const & object = m_adventure_map.get_object( object_id );

				if ( !object.is_visible_to( m_team_view ))
					continue;

				t_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
				t_screen_point obj_screen_point = m_adventure_map.get_screen_point( object_pos ) - m_view_pos;
				obj_screen_point.y -= m_adventure_map.get_object_height( object_id );
				t_screen_rect rel_cell_rect = cell_screen_rect - obj_screen_point;
				t_uint32 last_update_time = get_last_update_time( object_id, object );
				t_screen_rect image_rect = object.get_rect( last_update_time );
				if ( intersect( rel_cell_rect, image_rect ) )
				{
					object.draw_subimage_to(
						subimage_info_iter->subimage_num,
						last_update_time,
						intersection( rel_cell_rect, image_rect ),
						*m_back_buffer_ptr,
						obj_screen_point );
				}
			}

			// Draw "auras" (faint images of the object showing through any obstacles)
			bool have_obstacle = false;
			t_view_cell::t_const_object_subimage_info_reverse_iterator subimage_info_rev_iter = view_cell.object_subimage_rbegin();
			for ( ; subimage_info_rev_iter != view_cell.object_subimage_rend(); ++subimage_info_rev_iter )
			{
				t_object_id object_id = subimage_info_rev_iter->object_id;
				t_object const & object = m_adventure_map.get_object( object_id );
				int subimage_num = subimage_info_rev_iter->subimage_num;

				if ( object.subimage_is_underlay( subimage_num ) )
					break;

				if ( !object.is_visible_to( m_team_view ))
					continue;

				t_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
				t_screen_point obj_screen_point = m_adventure_map.get_screen_point( object_pos ) - m_view_pos;
				obj_screen_point.y -= m_adventure_map.get_object_height( object_id );
				t_screen_rect rel_cell_rect = cell_screen_rect - obj_screen_point;
				t_uint32 last_update_time = get_last_update_time( object_id, object );
				t_screen_rect image_rect = object.get_rect( last_update_time );
				if ( intersect( rel_cell_rect, image_rect ) )
				{
					static const int k_half_alpha = 8;	// Where should this constant really live?

					if (object.visible_through_obstacles( subimage_num )) {

						if (have_obstacle) {

							object.draw_subimage_to(
								subimage_num,
								last_update_time,
								intersection( rel_cell_rect, image_rect ),
								*m_back_buffer_ptr,
								obj_screen_point,
								k_half_alpha );
						}
					} else {
						have_obstacle = true;
					}
				}
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_right_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.top >= map_view_rect.top );
	assert( clip_rect.bottom <= map_view_rect.bottom );

	t_screen_rect edge_rect = clip_rect;
	if ( edge_rect.left < map_view_rect.right )
		edge_rect.left = map_view_rect.right;

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_right();

	int frame_width = frame_layer.get_width();
	if ( edge_rect.right > map_view_rect.right + frame_width )
	{
		t_screen_rect fill_rect = edge_rect;
		if ( fill_rect.left < map_view_rect.right + frame_width )
			fill_rect.left = map_view_rect.right + frame_width;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		edge_rect.right = fill_rect.left;
		if ( edge_rect.width() < 0 )
			return;
	}
	assert( edge_rect.width() <= frame_width );

	int frame_height = frame_layer.get_height();

	int frame_y = map_view_rect.top + ( ( edge_rect.top - map_view_rect.top ) / frame_height ) * frame_height;
	assert( frame_y + frame_height > edge_rect.top );

	t_screen_rect dest_rect( edge_rect.left, edge_rect.top, edge_rect.right, frame_y + frame_height );
	int source_x = edge_rect.left - map_view_rect.right;
	while ( true )
	{
		bool last;
		if ( dest_rect.bottom >= edge_rect.bottom )
		{
			dest_rect.bottom = edge_rect.bottom;
			last = true;
		}
		else
			last = false;

		frame_layer.draw_to(
			t_screen_rect( t_screen_point( source_x, dest_rect.top - frame_y ), dest_rect.size() ),
			*m_back_buffer_ptr,
			dest_rect.top_left() );

		if ( last )
			break;

		dest_rect.top = frame_y = dest_rect.bottom;
		dest_rect.bottom += frame_height;
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_shroud( t_screen_rect const & clip_rect )
{
	// If there is no player view specified, this would be the map
	// editor and we don't want to draw a shroud in that case so bail
	if( m_team_view < 0 )
		return;

	t_shroud_bitmap const & shroud_texture = get_shroud_texture();

	// Blt the shroud to the back buffer
	t_screen_rect quadrant_rect;
	quadrant_rect.left = 0;
	quadrant_rect.top = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.bottom = m_view_size.y - m_buffer_origin.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		bit_blt_shroud_with_mask(
			*m_back_buffer_ptr,
			rect.top_left(),
			shroud_texture,
			rect.top_left() + m_view_pos,
			*m_shroud_mask_ptr,
			rect.top_left() + m_buffer_origin,
			rect.size() );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.x -= m_view_size.x;
		bit_blt_shroud_with_mask(
			*m_back_buffer_ptr,
			rect.top_left(),
			shroud_texture,
			rect.top_left() + m_view_pos,
			*m_shroud_mask_ptr,
			dest_pos,
			rect.size() );
	}

	quadrant_rect.left = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.top = quadrant_rect.bottom;
	quadrant_rect.bottom = m_view_size.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.y -= m_view_size.y;
		bit_blt_shroud_with_mask(
			*m_back_buffer_ptr,
			rect.top_left(),
			shroud_texture,
			rect.top_left() + m_view_pos,
			*m_shroud_mask_ptr,
			dest_pos,
			rect.size() );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin - m_view_size;
		bit_blt_shroud_with_mask(
			*m_back_buffer_ptr,
			rect.top_left(),
			shroud_texture,
			rect.top_left() + m_view_pos,
			*m_shroud_mask_ptr,
			dest_pos,
			rect.size() );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_terrain( t_screen_rect const & clip_rect )
{
	// Blt the terrain from the terrain buffer
	t_screen_rect quadrant_rect;
	quadrant_rect.left = 0;
	quadrant_rect.top = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.bottom = m_view_size.y - m_buffer_origin.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		bit_blt< t_back_buffer::t_pixel >(
			*m_back_buffer_ptr,
			rect.top_left(),
			*m_terrain_buffer_ptr,
			rect.top_left() + m_buffer_origin,
			rect.size() );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.x -= m_view_size.x;
		bit_blt< t_back_buffer::t_pixel >(
			*m_back_buffer_ptr,
			rect.top_left(),
			*m_terrain_buffer_ptr,
			dest_pos,
			rect.size() );
	}

	quadrant_rect.left = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.top = quadrant_rect.bottom;
	quadrant_rect.bottom = m_view_size.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.y -= m_view_size.y;
		bit_blt< t_back_buffer::t_pixel >(
			*m_back_buffer_ptr,
			rect.top_left(),
			*m_terrain_buffer_ptr,
			dest_pos,
			rect.size() );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin - m_view_size;
		bit_blt< t_back_buffer::t_pixel >(
			*m_back_buffer_ptr,
			rect.top_left(),
			*m_terrain_buffer_ptr,
			dest_pos,
			rect.size() );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_to_shroud_mask( 
	t_screen_rect const &	src_rect,
	t_screen_point const &	dest_pos )
{
	assert( m_team_view >= 0 );

	t_screen_point adjusted_view_pos = m_view_pos + src_rect.top_left() - dest_pos;
	t_screen_rect adjusted_src_rect( dest_pos, src_rect.size() );

	t_screen_point map_view_size = m_adventure_map.get_view_size();
	t_screen_rect map_view_rect( 0, -k_max_tile_vertex_height, map_view_size.x, map_view_size.y );
	map_view_rect -= adjusted_view_pos;

	// If the specified rect is completely off of the map area simply fill the
	// entire rect with zero
	if ( !intersect( adjusted_src_rect, map_view_rect ) )
	{
		fill( *m_shroud_mask_ptr, adjusted_src_rect, t_uint8( 0 ) );
		return;
	}

	// If the rect extends off of the left edge, fill the exposed left edge with zero
	if ( adjusted_src_rect.left < map_view_rect.left )
	{
		t_screen_rect	fill_rect(
							adjusted_src_rect.left,
							adjusted_src_rect.top,
							map_view_rect.left,
							adjusted_src_rect.bottom );
		fill( *m_shroud_mask_ptr, fill_rect, t_uint8( 0 ) );
		adjusted_src_rect.left = map_view_rect.left;
	}

	// If the rect extends off of the right edge, fill the exposed right edge with zero
	if ( adjusted_src_rect.right > map_view_rect.right )
	{
		t_screen_rect	fill_rect(
							map_view_rect.right,
							adjusted_src_rect.top,
							adjusted_src_rect.right,
							adjusted_src_rect.bottom );
		fill( *m_shroud_mask_ptr, fill_rect, t_uint8( 0 ) );
		adjusted_src_rect.right = map_view_rect.right;
	}

	// If the rect extends off of the top edge, fill the exposed top edge with zero
	if ( adjusted_src_rect.top < map_view_rect.top )
	{
		t_screen_rect	fill_rect(
							adjusted_src_rect.left,
							adjusted_src_rect.top,
							adjusted_src_rect.right,
							map_view_rect.top );
		fill( *m_shroud_mask_ptr, fill_rect, t_uint8( 0 ) );
		adjusted_src_rect.top = map_view_rect.top;
	}

	// If the rect extends off of the bottom edge, fill the exposed bottom edge with zero
	if ( adjusted_src_rect.bottom > map_view_rect.bottom )
	{
		t_screen_rect	fill_rect(
							adjusted_src_rect.left,
							map_view_rect.bottom,
							adjusted_src_rect.right,
							adjusted_src_rect.bottom );
		fill( *m_shroud_mask_ptr, fill_rect, t_uint8( 0 ) );
		adjusted_src_rect.bottom = map_view_rect.bottom;
	}

	// Setting the team view to 'k_player_gray' alway shrouds everything
	if ( m_team_view == k_player_gray )
	{
		fill( *m_shroud_mask_ptr, adjusted_src_rect, 15 );
		return;
	}

	enumerate_tile_points_within_screen_rect(
		t_draw_terrain_map_adapter( *this ),
		m_view_level,
		adjusted_src_rect + adjusted_view_pos,
		adjusted_src_rect.top_left(),
		t_draw_tile_shroud_mask_func( *m_shroud_mask_ptr, m_adventure_map, m_team_view ) );

	// Draw the top margin of the map
	if ( src_rect.top + m_view_pos.y < 0 )
	{
		int margin_column_start = ( adjusted_src_rect.left + adjusted_view_pos.x ) / k_margin_column_width;
		int margin_column_end = ( ( adjusted_src_rect.right + adjusted_view_pos.x ) + k_margin_column_width - 1 ) / k_margin_column_width;

		int margin_column_num;
		for ( margin_column_num = margin_column_start; margin_column_num < margin_column_end; ++margin_column_num )
			draw_top_margin_column_shroud_mask( margin_column_num, adjusted_src_rect, adjusted_view_pos );
	}

	// Draw the bottom margin of the map
	if ( src_rect.bottom + m_view_pos.y > m_adventure_map.get_view_size().y - k_max_tile_vertex_height )
	{
		int margin_column_start = ( adjusted_src_rect.left + adjusted_view_pos.x ) / k_margin_column_width;
		int margin_column_end = ( ( adjusted_src_rect.right + adjusted_view_pos.x ) + k_margin_column_width - 1 ) / k_margin_column_width;

		int margin_column_num;
		for ( margin_column_num = margin_column_start; margin_column_num < margin_column_end; ++margin_column_num )
			draw_bottom_margin_column_shroud_mask( margin_column_num, adjusted_src_rect, adjusted_view_pos );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_top_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.top < map_view_rect.top );
	assert( clip_rect.width() > 0 );

	t_screen_rect edge_rect = clip_rect;
	if ( edge_rect.bottom > map_view_rect.top )
		edge_rect.bottom = map_view_rect.top;

	if ( edge_rect.left < map_view_rect.left )
	{
		draw_top_left_edge( edge_rect, map_view_rect );
		edge_rect.left = map_view_rect.left;
		if ( edge_rect.width() <= 0 )
			return;
	}

	if ( edge_rect.right > map_view_rect.right )
	{
		draw_top_right_edge( edge_rect, map_view_rect );
		edge_rect.right = map_view_rect.right;
		if ( edge_rect.width() <= 0 )
			return;
	}

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_top();

	int frame_height = frame_layer.get_height();
	if ( edge_rect.top < map_view_rect.top - frame_height )
	{
		t_screen_rect fill_rect = edge_rect;
		if ( fill_rect.bottom > map_view_rect.top - frame_height )
			fill_rect.bottom = map_view_rect.top - frame_height;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		edge_rect.top = fill_rect.bottom;
		if ( edge_rect.height() < 0 )
			return;
	}
	assert( edge_rect.height() <= frame_height );

	int frame_width = frame_layer.get_width();

	int frame_x = map_view_rect.left + ( ( edge_rect.left - map_view_rect.left ) / frame_width ) * frame_width;
	assert( frame_x + frame_width > edge_rect.left );

	t_screen_rect dest_rect( edge_rect.left, edge_rect.top, frame_x + frame_width, edge_rect.bottom );
	int source_y = edge_rect.top - ( map_view_rect.top - frame_height );
	while ( true )
	{
		bool last;
		if ( dest_rect.right >= edge_rect.right )
		{
			dest_rect.right = edge_rect.right;
			last = true;
		}
		else
			last = false;

		frame_layer.draw_to(
			t_screen_rect( t_screen_point( dest_rect.left - frame_x, source_y ), dest_rect.size() ),
			*m_back_buffer_ptr,
			dest_rect.top_left() );

		if ( last )
			break;

		dest_rect.left = frame_x = dest_rect.right;
		dest_rect.right += frame_width;
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_top_left_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.left < map_view_rect.left );
	assert( clip_rect.bottom <= map_view_rect.top );

	t_screen_rect corner_rect = clip_rect;
	if ( corner_rect.right > map_view_rect.left )
		corner_rect.right = map_view_rect.left;

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_top_left();

	int frame_height = frame_layer.get_height();
	if ( corner_rect.top < map_view_rect.top - frame_height )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.bottom > map_view_rect.top - frame_height )
			fill_rect.bottom = map_view_rect.top - frame_height;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.top = fill_rect.bottom;
		if ( corner_rect.height() <= 0 )
			return;
	}

	int frame_width = frame_layer.get_width();
	if ( corner_rect.left < map_view_rect.left - frame_width )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.right > map_view_rect.left - frame_width )
			fill_rect.right = map_view_rect.left - frame_width;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.left = fill_rect.right;
		if ( corner_rect.width() <= 0 )
			return;
	}

	assert( corner_rect.width() <= frame_width );
	assert( corner_rect.height() <= frame_height );

	t_screen_rect source_rect = corner_rect;
	source_rect -= map_view_rect.top_left() - t_screen_point( frame_width, frame_height );
	frame_layer.draw_to( source_rect, *m_back_buffer_ptr, corner_rect.top_left() );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_passability(
	t_screen_rect const &	clip_rect )
{
	enumerate_tile_points_within_screen_rect(
		t_draw_terrain_map_adapter( *this ),
		m_view_level,
		clip_rect + m_view_pos,
		clip_rect.top_left(),
		t_draw_tile_passability_func( m_adventure_map, *m_back_buffer_ptr ) );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_top_margin_column(
	t_screen_rect const &	clip_rect,
	int						margin_column_num )
{
	t_internal_map_data & internal_map_data = get_internal_map_data();

	int left_height = internal_map_data.get_top_margin_point_height( m_view_level, margin_column_num );
	int right_height = internal_map_data.get_top_margin_point_height( m_view_level, margin_column_num + 1 );

	t_screen_rect column_rect;
	column_rect.left = margin_column_num * k_margin_column_width;
	column_rect.right = column_rect.left + k_margin_column_width;
	column_rect.top = -k_max_tile_vertex_height;
	column_rect.bottom = -( left_height > right_height ? left_height : right_height );
	column_rect -= m_view_pos;

	t_bitmap_layer const & texture = m_margin_texture_layers.get_top();

	if ( intersect( column_rect, clip_rect ) )
	{
		t_screen_rect fill_rect = intersection( column_rect, clip_rect );
		t_screen_point source_point = fill_rect.top_left() + m_view_pos;
		wrap_draw( *m_back_buffer_ptr, fill_rect.top_left(), texture, source_point, fill_rect.size() );
	}

	if ( left_height == right_height )
		return;

	column_rect.top = column_rect.bottom;
	if ( left_height > right_height )
		column_rect.bottom += left_height - right_height;
	else
		column_rect.bottom += right_height - left_height;

	t_screen_point source_point = column_rect.top_left() + m_view_pos;

	if ( left_height > right_height )
		wrap_draw_top_right( *m_back_buffer_ptr, column_rect, texture, source_point, clip_rect );
	else
		wrap_draw_top_left( *m_back_buffer_ptr, column_rect, texture, source_point, clip_rect );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_top_margin_column_shroud_mask(
	int						margin_column_num,
	t_screen_rect const &	rect,
	t_screen_point const &	view_pos )
{
	assert( m_team_view >= 0 );

	t_internal_map_data & internal_map_data = get_internal_map_data();

	int left_height = internal_map_data.get_top_margin_point_height( m_view_level, margin_column_num );
	int right_height = internal_map_data.get_top_margin_point_height( m_view_level, margin_column_num + 1 );

	t_screen_rect column_rect;
	column_rect.left = margin_column_num * k_margin_column_width;
	column_rect.right = column_rect.left + k_margin_column_width;
	column_rect.top = -k_max_tile_vertex_height;
	column_rect.bottom = -( left_height > right_height ? right_height : left_height );
	column_rect -= view_pos;

	if ( !intersect( column_rect, rect ) )
		return;

	t_level_map_point_2d	tile_point(
								compute_top_margin_column_tile_point(
									m_adventure_map,
									margin_column_num ),
								m_view_level );

	bool left_half = ( margin_column_num & 1 ) == 0;

	t_bitmap_1d< t_uint8 > & column_shroud_mask = get_margin_column_shroud_mask();

	draw_tile_half_horizontal_shroud_mask(
		m_adventure_map,
		m_team_view,
		tile_point,
		left_half,
		column_shroud_mask );

	column_rect.bottom -= left_height > right_height ? left_height - right_height : right_height - left_height;

	if ( intersect( column_rect, rect ) )
	{
		t_screen_rect clipped_column_rect = intersection( column_rect, rect );
		bit_blt_horizontal_source(
			*m_shroud_mask_ptr,
			clipped_column_rect.top_left(),
			column_shroud_mask,
			clipped_column_rect.left - column_rect.left,
			clipped_column_rect.size() );
	}

	column_rect.top = column_rect.bottom;
	if ( left_height > right_height )
	{
		column_rect.bottom = column_rect.top + ( left_height - right_height );
		bit_blt_horizontal_source_top_right(
			*m_shroud_mask_ptr,
			column_rect,
			column_shroud_mask,
			0,
			rect );
	}
	else
	{
		column_rect.bottom = column_rect.top + ( right_height - left_height );
		bit_blt_horizontal_source_top_left(
			*m_shroud_mask_ptr,
			column_rect,
			column_shroud_mask,
			0,
			rect );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::draw_top_right_edge(
	t_screen_rect const &	clip_rect,
	t_screen_rect const &	map_view_rect )
{
	assert( clip_rect.right > map_view_rect.right );
	assert( clip_rect.bottom <= map_view_rect.top );

	t_screen_rect corner_rect = clip_rect;
	if ( corner_rect.left < map_view_rect.right )
		corner_rect.left = map_view_rect.right;

	t_bitmap_layer const & frame_layer = m_border_frame_layers.get_top_right();

	int frame_height = frame_layer.get_height();
	if ( corner_rect.top < map_view_rect.top - frame_height )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.bottom > map_view_rect.top - frame_height )
			fill_rect.bottom = map_view_rect.top - frame_height;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.top = fill_rect.bottom;
		if ( corner_rect.height() <= 0 )
			return;
	}

	int frame_width = frame_layer.get_width();
	if ( corner_rect.right > map_view_rect.right + frame_width )
	{
		t_screen_rect fill_rect = corner_rect;
		if ( fill_rect.left < map_view_rect.right + frame_width )
			fill_rect.left = map_view_rect.right + frame_width;
		fill( *m_back_buffer_ptr, fill_rect, t_back_buffer::t_pixel( 0 ) );
		corner_rect.right = fill_rect.left;
		if ( corner_rect.width() <= 0 )
			return;
	}

	assert( corner_rect.width() <= frame_width );
	assert( corner_rect.height() <= frame_height );

	t_screen_rect source_rect = corner_rect;
	source_rect -= map_view_rect.top_right() - t_screen_point( 0, frame_height );
	frame_layer.draw_to( source_rect, *m_back_buffer_ptr, corner_rect.top_left() );
}

/////////////////////////////////////////////////////////////////////////////
t_screen_point t_map_renderer::t_impl::get_adv_object_screen_point(
	t_object_id	object_id ) const
{
	t_level_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
	t_screen_point result = m_adventure_map.get_screen_point( object_pos ) - m_view_pos;
	result.y -= m_adventure_map.get_object_height( object_id );
	return result;
}

/////////////////////////////////////////////////////////////////////////////
t_uint32 t_map_renderer::t_impl::get_last_update_time(
	t_object_id			object_id,
	t_object const &	object ) const
{
	if ( !object.animates() || m_animating_object_ids.find( object_id ) == m_animating_object_ids.end() )
		return 0;
	return m_last_update_time;
}

/////////////////////////////////////////////////////////////////////////////
t_uint32 t_map_renderer::t_impl::get_last_update_time(
	t_object_id	object_id ) const
{
	if ( m_animating_object_ids.find( object_id ) == m_animating_object_ids.end() )
		return 0;
	return m_last_update_time;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::invalidate(
	t_object_height_delta_set const &	changed_object_height_deltas,
	t_clip_list &						result )
{
	if ( m_team_view == k_player_gray )
		return;

	t_screen_rect clip_rect( 0, 0, m_view_size.x, m_view_size.y );

	t_object_height_delta_set::const_iterator object_height_delta_iter = changed_object_height_deltas.begin();
	for ( ; object_height_delta_iter != changed_object_height_deltas.end(); ++object_height_delta_iter )
	{
		t_abstract_adventure_map::t_object_id object_id = object_height_delta_iter->first;

		// Skip objects which should be obscurred by the fog of war
		t_abstract_adventure_map::t_object const & object = m_adventure_map.get_object( object_id );
		if ( !object.is_visible_to( m_team_view ) )
			continue;

		int height_delta = object_height_delta_iter->second;
		int height = m_adventure_map.get_object_height( object_id );

		t_screen_point base_point = m_adventure_map.get_screen_point( m_adventure_map.get_object_pos( object_id ) ) - m_view_pos;
		base_point.y -= height;

		t_uint32 last_update_time = get_last_update_time( object_id, object );
		t_screen_rect object_rect = object.get_rect( last_update_time ) + base_point;
		t_screen_rect shadow_rect = object.get_shadow_rect( last_update_time ) + base_point;

		if ( intersect( object_rect, clip_rect ) )
			result += intersection( object_rect, clip_rect );

		if ( intersect( shadow_rect, clip_rect ) )
			result += intersection( shadow_rect, clip_rect );

		object_rect.top += height_delta;
		object_rect.bottom += height_delta;
		shadow_rect.top += height_delta;
		shadow_rect.bottom += height_delta;

		if ( intersect( object_rect, clip_rect ) )
			result += intersection( object_rect, clip_rect );

		if ( intersect( shadow_rect, clip_rect ) )
			result += intersection( shadow_rect, clip_rect );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::invalidate_object(
	t_object const &				object,
	t_level_map_point_3d const &	object_pos,
	t_uint32						current_time,
	t_clip_list &					clip_list )
{
	assert( object_pos.level == m_view_level );

	t_screen_rect clip_rect( 0, 0, m_view_size.x, m_view_size.y );

	t_screen_point base_point = m_adventure_map.get_screen_point( object_pos ) - m_view_pos;
	base_point.y -= object_pos.height;

	t_screen_rect object_extent = object.get_rect( current_time ) + base_point;
	if ( intersect( object_extent, clip_rect ) )
		clip_list += intersection( object_extent, clip_rect );

	object_extent = object.get_shadow_rect( current_time ) + base_point;
	if ( intersect( object_extent, clip_rect ) )
		clip_list += intersection( object_extent, clip_rect );

	// If viewing passability, the object's footprint must be invalidated
	if ( m_view_passability )
	{
		t_tile_point_set const & footprint_point_set =
			build_map_rect_point_set( object_pos, object.get_footprint().get_size() );
		clip_list += build_clip_list( footprint_point_set );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::invalidate_object(
	t_object_id		object_id,
	t_uint32		current_time,
	t_clip_list &	clip_list )
{
	t_level_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
	if ( object_pos.level != m_view_level )
		return;

	invalidate_object(
		m_adventure_map.get_object( object_id ),
		t_level_map_point_3d(
			object_pos,
			m_adventure_map.get_object_height( object_id ) ),
		current_time,
		clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::invalidate_object_if_visible(
	t_object_id		object_id,
	t_uint32		current_time,
	t_clip_list &	clip_list )
{
	if ( m_team_view == k_player_gray )
		return;

	t_level_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
	if ( object_pos.level != m_view_level )
		return;

	t_object const & object = m_adventure_map.get_object( object_id );
	if ( object.is_visible_to( m_team_view ))
	{
		invalidate_object(
			object,
			t_level_map_point_3d(
				object_pos,
				m_adventure_map.get_object_height( object_id ) ),
			current_time,
			clip_list );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_adv_object_moved( t_object_id object_id )
{
	t_clip_list clip_list;
	record_if_animating( object_id );
	invalidate_object_if_visible( object_id, get_last_update_time( object_id ), clip_list );
	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_adv_object_moved(
	t_object_id							object_id,
	int									level,
	t_tile_point_set const &			changed_tile_points,
	std::map< int, int > const &		top_margin_column_height_deltas,
	std::map< int, int > const &		bottom_margin_column_height_deltas,
	t_object_height_delta_set const &	changed_object_height_deltas )
{
	t_clip_list clip_list;

	record_if_animating( object_id );
	invalidate_object_if_visible( object_id, get_last_update_time( object_id ), clip_list );

	if ( level == m_view_level )
	{
		reevaluate_animating_object_ids( changed_object_height_deltas );

		on_terrain_changed_helper(
			changed_tile_points,
			top_margin_column_height_deltas,
			bottom_margin_column_height_deltas,
			clip_list );
		invalidate( changed_object_height_deltas, clip_list );
	}

	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_adv_object_placed( t_object_id object_id )
{
	t_clip_list clip_list;
	record_if_animating( object_id );
	invalidate_object_if_visible( object_id, get_last_update_time( object_id ), clip_list );
	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_adv_object_placed(
	t_object_id							object_id,
	int									level,
	t_tile_point_set const &			changed_tile_points,
	std::map< int, int > const &		top_margin_column_height_deltas,
	std::map< int, int > const &		bottom_margin_column_height_deltas,
	t_object_height_delta_set const &	changed_object_height_deltas )
{
	t_clip_list clip_list;

	record_if_animating( object_id );
	invalidate_object_if_visible( object_id, get_last_update_time( object_id ), clip_list );

	if ( level == m_view_level )
	{
		reevaluate_animating_object_ids( changed_object_height_deltas );

		on_terrain_changed_helper(
			changed_tile_points,
			top_margin_column_height_deltas,
			bottom_margin_column_height_deltas,
			clip_list );
		invalidate( changed_object_height_deltas, clip_list );
	}

	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_adv_object_removed(
	t_object const &				object,
	t_level_map_point_2d const &	object_pos )
{
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_idle()
{
	t_uint32 current_time = get_time();
	
	//t_clip_list				m_refresh_list;
	//t_clip_list				m_intersect_list;

	//t_clip_list m_refresh_list;
	
	m_refresh_list.clear();

	t_screen_rect clip_rect( t_screen_point( 0, 0 ), m_view_size );

	// Run through the objects on the map to determine which objects need to
	// be cycled.
	t_object_id_set::const_iterator object_id_iter = m_animating_object_ids.begin();
	t_object_id_set::const_iterator object_id_end = m_animating_object_ids.end();
	for (	;
			object_id_iter != object_id_end;
			++object_id_iter )
	{
		t_object_id object_id = *object_id_iter;

		t_abstract_adv_object const & object = m_adventure_map.get_object( object_id );

		// Add the shadow to the refresh list
		if ( object.needs_redrawing( m_last_update_time, current_time ) )
		{
			t_screen_point object_screen_point = get_adv_object_screen_point( object_id );
			t_screen_rect shadow_rect = object.get_shadow_rect( m_last_update_time ) + object_screen_point;

			if ( intersect( shadow_rect, clip_rect ) )
			{
				//m_refresh_list += intersection( shadow_rect, clip_rect );
				dirty(intersection( shadow_rect, clip_rect ));
			}

			shadow_rect = object.get_shadow_rect( current_time ) + object_screen_point;

			if ( intersect( shadow_rect, clip_rect ) )
			{
				//m_refresh_list += intersection( shadow_rect, clip_rect );
				dirty(intersection( shadow_rect, clip_rect ));
			}
		}

		// Add the object's subimages to the refresh list
		int subimage_count = object.get_subimage_count();
		int subimage_num;
		for ( subimage_num = 0; subimage_num < subimage_count; ++subimage_num )
		{
			if (	!object.subimage_needs_redrawing(
						subimage_num,
						m_last_update_time,
						current_time ) )
				continue;

			t_screen_point object_screen_point = get_adv_object_screen_point( object_id );
			t_screen_rect subimage_rect = object.get_subimage_rect( subimage_num, m_last_update_time ) + object_screen_point;

			if ( intersect( subimage_rect, clip_rect ) )
			{
				//m_refresh_list += intersection( subimage_rect, clip_rect );
				dirty(intersection( subimage_rect, clip_rect ));
			}

			subimage_rect = object.get_subimage_rect( subimage_num, current_time ) + object_screen_point;
			
			if ( intersect( subimage_rect, clip_rect ) )
			{
				//m_refresh_list += intersection( subimage_rect, clip_rect );
				dirty(intersection( subimage_rect, clip_rect ));
			}
		}
	}

	m_last_update_time = current_time;

	//dirty( m_refresh_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_moving_adv_object( t_object_id object_id )
{
	t_clip_list clip_list;
	invalidate_object_if_visible( object_id, get_last_update_time( object_id ), clip_list );
	remove_animating_object_id( object_id );
	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_multiple_adv_objects_removed()
{
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_removing_adv_object( t_object_id object_id )
{
	t_clip_list clip_list;
	invalidate_object_if_visible( object_id, get_last_update_time( object_id ), clip_list );
	remove_animating_object_id( object_id );
	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_removing_multiple_adv_objects(
	t_object_id_set const &	object_ids )
{
	t_object_id_set::const_iterator object_id_end = object_ids.end();
	t_object_id_set::const_iterator object_id_iter = object_ids.begin();
	for ( ; object_id_iter != object_id_end; ++object_id_iter )
		on_removing_adv_object( *object_id_iter );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_rock_terrain_changed(
	int							level,
	t_tile_point_set const &	changed_tile_points,
	t_object_id_set const &		placed_object_ids )
{
	t_clip_list clip_list;

	t_object_id_set::const_iterator object_id_end = placed_object_ids.end();
	t_object_id_set::const_iterator object_id_iter = placed_object_ids.begin();
	for ( ; object_id_iter != object_id_end; ++object_id_iter )
	{
		record_if_animating( *object_id_iter );
		invalidate_object_if_visible( *object_id_iter, get_last_update_time( *object_id_iter ), clip_list );
	}

	if ( level == m_view_level )
		refresh_terrain( changed_tile_points, clip_list );

	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_terrain_changed(
	int								level,
	t_tile_point_set const &		changed_tile_points,
	std::map< int, int > const &	top_margin_column_height_deltas,
	std::map< int, int > const &	bottom_margin_column_height_deltas )
{
	if ( level != m_view_level )
		return;

	t_clip_list clip_list;
	on_terrain_changed_helper(
		changed_tile_points,
		top_margin_column_height_deltas,
		bottom_margin_column_height_deltas,
		clip_list );
	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_terrain_changed(
	int									level,
	t_tile_point_set const &			changed_tile_points,
	std::map< int, int > const &		top_margin_column_height_deltas,
	std::map< int, int > const &		bottom_margin_column_height_deltas,
	t_object_height_delta_set const &	changed_object_height_deltas )
{
	if ( level != m_view_level )
		return;

	reevaluate_animating_object_ids( changed_object_height_deltas );

	t_clip_list clip_list;
	on_terrain_changed_helper(
		changed_tile_points,
		top_margin_column_height_deltas,
		bottom_margin_column_height_deltas,
		clip_list );
	invalidate( changed_object_height_deltas, clip_list );
	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_terrain_changed_helper(
	t_tile_point_set const &		changed_tile_points,
	std::map< int, int > const &	top_margin_column_height_deltas,
	std::map< int, int > const &	bottom_margin_column_height_deltas,
	t_clip_list &					result )
{
	refresh_terrain( changed_tile_points, result );

	t_internal_map_data const & internal_map_data = get_internal_map_data();
	t_screen_rect clip_rect( 0, 0, m_view_size.x, m_view_size.y );

	t_screen_rect uncovered_rect;
	std::map< int, int >::const_iterator margin_column_height_delta_iter = top_margin_column_height_deltas.begin();
	for (	;
			margin_column_height_delta_iter != top_margin_column_height_deltas.end();
			++margin_column_height_delta_iter )
	{
		int margin_column_num = margin_column_height_delta_iter->first;
		assert( margin_column_num >= 0 && margin_column_num < m_adventure_map.get_size() );

		int height_delta = margin_column_height_delta_iter->second;
		assert( height_delta < 0 );

		uncovered_rect.left = margin_column_num * k_margin_column_width - m_view_pos.x;
		if ( uncovered_rect.left >= clip_rect.right )
			continue;

		uncovered_rect.right = uncovered_rect.left + k_margin_column_width;
		if ( uncovered_rect.right <= clip_rect.left )
			continue;

		uncovered_rect.bottom = -internal_map_data.get_top_margin_column_height( m_view_level, margin_column_num ) - m_view_pos.y;
		if ( uncovered_rect.bottom <= clip_rect.top )
			continue;

		uncovered_rect.top = uncovered_rect.bottom + height_delta;
		if ( uncovered_rect.top >= clip_rect.bottom )
			continue;

		result += intersection( uncovered_rect, clip_rect );
	}

	int view_height = m_adventure_map.get_view_size().y;
	for (	margin_column_height_delta_iter = bottom_margin_column_height_deltas.begin();
			margin_column_height_delta_iter != bottom_margin_column_height_deltas.end();
			++margin_column_height_delta_iter )
	{
		int margin_column_num = margin_column_height_delta_iter->first;
		assert( margin_column_num >= 0 && margin_column_num < m_adventure_map.get_size() );

		int height_delta = margin_column_height_delta_iter->second;
		assert( height_delta > 0 );

		uncovered_rect.left = margin_column_num * k_margin_column_width - m_view_pos.x;
		if ( uncovered_rect.left >= clip_rect.right )
			continue;

		uncovered_rect.right = uncovered_rect.left + k_margin_column_width;
		if ( uncovered_rect.right <= clip_rect.left )
			continue;

		uncovered_rect.top = view_height - internal_map_data.get_bottom_margin_column_height( m_view_level, margin_column_num ) - m_view_pos.y;
		if ( uncovered_rect.top >= clip_rect.bottom )
			continue;

		uncovered_rect.bottom = uncovered_rect.top + height_delta;
		if ( uncovered_rect.bottom <= clip_rect.top )
			continue;

		result += intersection( uncovered_rect, clip_rect );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::on_visibility_changed(
	int							team_num,
	int							level,
	t_visibility_set const &	changed_visibility_set,
	t_tile_point_set const &	changed_shroud_set )
{
	assert( team_num >= 0 );
	if ( team_num != m_team_view || level != m_view_level )
		return;

	t_clip_list clip_list;

	build_clip_list_for_shroud( changed_shroud_set ).swap( clip_list );
	
	t_clip_list::t_const_iterator rect_end = clip_list.end();
	t_clip_list::t_const_iterator rect_iter = clip_list.begin();
	for ( ; rect_iter != rect_end; ++rect_iter )
		update_shroud_mask( *rect_iter );

	adjust_object_animation_for_shroud_change( changed_shroud_set, clip_list );
	adjust_object_visibility_for_visibility_change( changed_visibility_set, clip_list );

	dirty( clip_list );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::record_animating_object_ids_in_rect(
	t_screen_rect const &	rect )
{
	if ( rect.width() <= 0 || rect.height() <= 0 )
		return;

	t_screen_point map_view_size = m_adventure_map.get_view_size();
	t_screen_rect map_view_rect( 0, -k_max_tile_vertex_height, map_view_size.x, map_view_size.y );
	map_view_rect -= m_view_pos;

	if ( !intersect( rect, map_view_rect ) )
		return;

	t_screen_rect adjusted_rect = intersection( rect, map_view_rect );
	adjusted_rect += m_view_pos;

	t_screen_rect	view_cell_rect(
						adjusted_rect.left >> k_adventure_map_view_cell_shift,
						adjusted_rect.top >> k_adventure_map_view_cell_shift,
						( adjusted_rect.right + k_adventure_map_view_cell_size - 1 ) >> k_adventure_map_view_cell_shift,
						( adjusted_rect.bottom + k_adventure_map_view_cell_size - 1 ) >> k_adventure_map_view_cell_shift );

	adjusted_rect -= m_view_pos;

	t_screen_point view_cell_point;
	for (	view_cell_point.y = view_cell_rect.top;
			view_cell_point.y < view_cell_rect.bottom;
			++view_cell_point.y )
	{
		for (	view_cell_point.x = view_cell_rect.left;
				view_cell_point.x < view_cell_rect.right;
				++view_cell_point.x )
		{
			typedef t_adv_map::t_view_cell t_view_cell;
			t_view_cell const & view_cell = m_adventure_map.get_view_cell( m_view_level, view_cell_point );

			t_view_cell::t_const_object_subimage_info_iterator subimage_info_iter =
				view_cell.object_subimage_begin();
			t_view_cell::t_const_object_subimage_info_iterator subimage_info_end =
				view_cell.object_subimage_end();
			for (	;
					subimage_info_iter != subimage_info_end;
					++subimage_info_iter )
			{
				t_object_id object_id = subimage_info_iter->object_id;
				t_object const & object = m_adventure_map.get_object( object_id );
				if ( !object.animates() || is_under_fog_of_war( object_id ) )
					continue;

				t_screen_point object_screen_point = get_adv_object_screen_point( object_id );
				t_screen_rect extent = object_screen_point + object.get_rect();

				if ( intersect( extent, adjusted_rect ) )
					add_animating_object_id( object_id );
			}

			t_view_cell::t_const_shadow_object_id_iterator shadow_id_iter =
				view_cell.shadow_begin();
			t_view_cell::t_const_shadow_object_id_iterator shadow_id_end =
				view_cell.shadow_end();
			for (	;
					shadow_id_iter != shadow_id_end;
					++shadow_id_iter )
			{
				t_object_id object_id = *shadow_id_iter;
				t_object const & object = m_adventure_map.get_object( object_id );
				if ( !object.animates() || is_under_fog_of_war( object_id ) )
					continue;

				t_screen_point object_screen_point = get_adv_object_screen_point( object_id );
				t_screen_rect shadow_extent = object_screen_point + object.get_shadow_rect();

				if ( intersect( shadow_extent, adjusted_rect ) )
					add_animating_object_id( object_id );
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
bool t_map_renderer::t_impl::record_if_animating(
	t_object_id object_id )
{
	t_level_map_point_2d object_pos = m_adventure_map.get_object_pos( object_id );
	if ( object_pos.level != m_view_level )
		return false;

	t_object const & object = m_adventure_map.get_object( object_id );

	if ( !object.animates() || is_under_fog_of_war( object_id ) )
		return false;

	t_screen_rect view_rect( t_screen_point( 0, 0 ), m_view_size );

	t_screen_point object_screen_point = get_adv_object_screen_point( object_id );

	t_screen_rect object_extent = object_screen_point + object.get_rect();
	if ( intersect( object_extent, view_rect ) )
		return add_animating_object_id( object_id );

	object_extent = object_screen_point + object.get_shadow_rect();
	if ( intersect( object_extent, view_rect ) )
		return add_animating_object_id( object_id );

	return false;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::reevaluate_animating_object_ids()
{
	t_screen_rect view_rect( t_screen_point( 0, 0 ), m_view_size );

	t_object_id_set::iterator object_id_iter = m_animating_object_ids.begin();
	while ( object_id_iter != m_animating_object_ids.end() )
	{
		t_object_id object_id = *object_id_iter;

		t_object const & object = m_adventure_map.get_object( object_id );
		t_screen_point object_screen_point = get_adv_object_screen_point( object_id );

		bool remove = false;
		t_screen_rect object_extent = object_screen_point + object.get_rect();
		if ( !intersect( object_extent, view_rect ) )
		{
			object_extent = object_screen_point + object.get_shadow_rect();
			if ( !intersect( object_extent, view_rect ) )
				remove = true;
		}

		if ( remove )
			object_id_iter = remove_animating_object_id( object_id_iter );
		else
			++object_id_iter;
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::reevaluate_animating_object_ids(
	 t_object_height_delta_set const &	changed_object_height_deltas )
{
	t_screen_rect view_rect( t_screen_point( 0, 0 ), m_view_size );

	t_object_height_delta_set::const_iterator object_height_delta_end = changed_object_height_deltas.end();
	t_object_height_delta_set::const_iterator object_height_delta_iter = changed_object_height_deltas.begin();
	for ( ; object_height_delta_iter != object_height_delta_end; ++object_height_delta_iter )
	{
		t_object_id object_id = object_height_delta_iter->first;
		t_object const & object = m_adventure_map.get_object( object_id );

		if ( !object.animates() || is_under_fog_of_war( object_id ) )
			return;

		t_screen_point object_screen_point = get_adv_object_screen_point( object_id );

		bool on_screen = false;
		t_screen_rect object_extent = object_screen_point + object.get_rect();
		if ( intersect( object_extent, view_rect ) )
			on_screen = true;
		else
		{
			object_extent = object_screen_point + object.get_shadow_rect();
			if ( intersect( object_extent, view_rect ) )
				on_screen = true;
		}

		if ( on_screen )
			add_animating_object_id( object_id );
		else
			remove_animating_object_id( object_id );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::refresh_terrain( t_clip_list const & clip_list )
{
	if ( !clip_list.empty() )
	{
		t_clip_list::t_const_iterator clip_list_iter = clip_list.begin();
		for ( ; clip_list_iter != clip_list.end(); ++clip_list_iter )
		{
			update_terrain_buffer( *clip_list_iter );
			update_shroud_mask( *clip_list_iter );
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::refresh_terrain(
	t_tile_point_set const &	tile_points,
	t_clip_list &				result )
{
	t_clip_list clip_list;
	build_clip_list( tile_points ).swap( clip_list );

	refresh_terrain( clip_list );

	if ( result.empty() )
		result.swap( clip_list );
	else
		result += clip_list;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::remove_animating_object_id(
	t_object_id	object_id )
{
	bool was_empty = m_animating_object_ids.empty();
	m_animating_object_ids.erase( object_id );
	if ( !was_empty && m_animating_object_ids.empty() && is_active() )
		suspend_idle_processing();
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_impl::t_object_id_set::iterator
t_map_renderer::t_impl::remove_animating_object_id(
	t_object_id_set::iterator iter )
{
	bool was_empty = m_animating_object_ids.empty();
	iter = m_animating_object_ids.erase( iter );
	if ( !was_empty && m_animating_object_ids.empty() && is_active() )
		suspend_idle_processing();
	return iter;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::update_shroud_mask( t_screen_rect const & clip_rect )
{
	if ( m_team_view < 0 || clip_rect.width() == 0 || clip_rect.height() == 0 )
		return;

	t_screen_rect quadrant_rect;
	quadrant_rect.left = 0;
	quadrant_rect.top = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.bottom = m_view_size.y - m_buffer_origin.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		draw_to_shroud_mask(
			rect,
			rect.top_left() + m_buffer_origin );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.x -= m_view_size.x;
		draw_to_shroud_mask(
			rect,
			dest_pos );
	}

	quadrant_rect.left = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.top = quadrant_rect.bottom;
	quadrant_rect.bottom = m_view_size.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.y -= m_view_size.y;
		draw_to_shroud_mask(
			rect,
			dest_pos );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		draw_to_shroud_mask(
			rect,
			rect.top_left() + m_buffer_origin - m_view_size );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::t_impl::update_terrain_buffer(
	t_screen_rect const &	clip_rect )
{
	if ( clip_rect.width() == 0 || clip_rect.height() == 0 )
		return;

	assert( m_terrain_buffer_ptr.get() != 0 );

	t_screen_rect quadrant_rect;
	quadrant_rect.left = 0;
	quadrant_rect.top = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.bottom = m_view_size.y - m_buffer_origin.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		::draw_terrain(
			t_draw_terrain_map_adapter( *this ), m_view_level, rect + m_view_pos,
			*m_terrain_buffer_ptr, rect.top_left() + m_buffer_origin,
			m_view_grid );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.x -= m_view_size.x;
		::draw_terrain(
			t_draw_terrain_map_adapter( *this ), m_view_level, rect + m_view_pos,
			*m_terrain_buffer_ptr, dest_pos, m_view_grid );
	}

	quadrant_rect.left = 0;
	quadrant_rect.right = m_view_size.x - m_buffer_origin.x;
	quadrant_rect.top = quadrant_rect.bottom;
	quadrant_rect.bottom = m_view_size.y;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		t_screen_point dest_pos = rect.top_left() + m_buffer_origin;
		dest_pos.y -= m_view_size.y;
		::draw_terrain(
			t_draw_terrain_map_adapter( *this ), m_view_level, rect + m_view_pos,
			*m_terrain_buffer_ptr, dest_pos, m_view_grid );
	}

	quadrant_rect.left = quadrant_rect.right;
	quadrant_rect.right = m_view_size.x;
	if ( intersect( quadrant_rect, clip_rect ) )
	{
		t_screen_rect rect = intersection( quadrant_rect, clip_rect );
		::draw_terrain(
			t_draw_terrain_map_adapter( *this ), m_view_level, rect + m_view_pos,
			*m_terrain_buffer_ptr, rect.top_left() + m_buffer_origin - m_view_size,
			m_view_grid );
	}
}

/////////////////////////////////////////////////////////////////////////////
// Details namespace

namespace map_renderer_details
{

	/////////////////////////////////////////////////////////////////////////////
	// t_internal_map_data members

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_adv_object_moved( t_object_id object_id )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_adv_object_moved( object_id );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_adv_object_moved(
		t_object_id							object_id,
		int									level,
		t_tile_point_set const &			changed_tile_points,
		t_object_height_delta_set const &	changed_object_height_deltas )
	{
		std::map< int, int > top_margin_column_height_deltas;
		std::map< int, int > bottom_margin_column_height_deltas;
		on_terrain_changed_helper(
			level,
			changed_tile_points,
			top_margin_column_height_deltas,
			bottom_margin_column_height_deltas );

		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
		{
			( *map_renderer_ptr_iter )->m_impl_ptr->on_adv_object_moved(
				object_id,
				level,
				changed_tile_points,
				top_margin_column_height_deltas,
				bottom_margin_column_height_deltas,
				changed_object_height_deltas );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_adv_object_placed( t_object_id object_id )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_adv_object_placed( object_id );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_adv_object_placed(
		t_object_id							object_id,
		int									level,
		t_tile_point_set const &			changed_tile_points,
		t_object_height_delta_set const &	changed_object_height_deltas )
	{
		std::map< int, int > top_margin_column_height_deltas;
		std::map< int, int > bottom_margin_column_height_deltas;
		on_terrain_changed_helper(
			level,
			changed_tile_points,
			top_margin_column_height_deltas,
			bottom_margin_column_height_deltas );

		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
		{
			( *map_renderer_ptr_iter )->m_impl_ptr->on_adv_object_placed(
				object_id,
				level,
				changed_tile_points,
				top_margin_column_height_deltas,
				bottom_margin_column_height_deltas,
				changed_object_height_deltas );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_adv_object_removed(
		t_object const &				object,
		t_level_map_point_2d const &	object_pos )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_adv_object_removed( object, object_pos );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_moving_adv_object( t_object_id object_id )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_moving_adv_object( object_id );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_multiple_adv_objects_removed()
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_multiple_adv_objects_removed();
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_removing_adv_object( t_object_id object_id )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_removing_adv_object( object_id );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_removing_multiple_adv_objects(
		t_object_id_set const &	object_ids )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->m_impl_ptr->on_removing_multiple_adv_objects( object_ids );
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_rock_terrain_changed(
		int							level,
		t_tile_point_set const &	changed_tile_points,
		t_object_id_set const &		placed_object_ids )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
		{
			( *map_renderer_ptr_iter )->m_impl_ptr->on_rock_terrain_changed(
				level,
				changed_tile_points,
				placed_object_ids );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_terrain_changed(
		int							level,
		t_tile_point_set const &	changed_tile_points )
	{
		std::map< int, int > top_margin_column_height_deltas;
		std::map< int, int > bottom_margin_column_height_deltas;
		on_terrain_changed_helper(
			level,
			changed_tile_points,
			top_margin_column_height_deltas,
			bottom_margin_column_height_deltas );

		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
		{
			( *map_renderer_ptr_iter )->m_impl_ptr->on_terrain_changed(
				level,
				changed_tile_points,
				top_margin_column_height_deltas,
				bottom_margin_column_height_deltas );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_terrain_changed(
		int									level,
		t_tile_point_set const &			changed_tile_points,
		t_object_height_delta_set const &	changed_object_height_deltas )
	{
		std::map< int, int > top_margin_column_height_deltas;
		std::map< int, int > bottom_margin_column_height_deltas;
		on_terrain_changed_helper(
			level,
			changed_tile_points,
			top_margin_column_height_deltas,
			bottom_margin_column_height_deltas );

		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
		{
			( *map_renderer_ptr_iter )->m_impl_ptr->on_terrain_changed(
				level,
				changed_tile_points,
				top_margin_column_height_deltas,
				bottom_margin_column_height_deltas,
				changed_object_height_deltas );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::on_visibility_changed(
		int							team_num,
		int							level,
		t_visibility_set const &	changed_visibility_set,
		t_tile_point_set const &	changed_shroud_set )
	{
		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
		{
			( *map_renderer_ptr_iter )->m_impl_ptr->on_visibility_changed(
				team_num,
				level,
				changed_visibility_set,
				changed_shroud_set );
		}
	}

	/////////////////////////////////////////////////////////////////////////////
	void t_internal_map_data::refresh()
	{
		compute_margin_point_heights();

		t_map_renderer_ptr_set::const_iterator map_renderer_ptr_iter = m_map_renderer_ptrs.begin();
		for ( ; map_renderer_ptr_iter != m_map_renderer_ptrs.end(); ++map_renderer_ptr_iter )
			( *map_renderer_ptr_iter )->refresh();
	}

} // Details namespace

/////////////////////////////////////////////////////////////////////////////
// t_map_renderer members


#pragma warning( push )
#pragma warning( disable: 4355 )
/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_map_renderer( t_adv_map const & adventure_map )
	:	m_impl_ptr( new t_impl( *this, 0, adventure_map ) )
{
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_map_renderer(
	t_client &				client,
	t_adv_map const &		adventure_map )
	:	m_impl_ptr( new t_impl( *this, &client, adventure_map ) )
{
}
#pragma warning( pop )

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::~t_map_renderer()
{
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_object_id t_map_renderer::adv_object_hit_test_helper(
	t_screen_point const &			point,
	t_abstract_exclude_func const &	exclude_func ) const
{
	typedef t_adv_map::t_view_cell t_view_cell;

	t_impl & impl = *m_impl_ptr;

	if ( impl.m_team_view == k_player_gray )
		return t_adv_map::k_invalid_object_id;

	// Check the visibility of the tile
	t_tile_visibility visibility = k_tile_visible;
	if ( impl.m_team_view >= 0 )
	{
		assert( impl.m_shroud_mask_ptr.get() != 0 );

		t_map_point_2d tile_point;
		if ( tile_hit_test( point, tile_point ) )
		{
			t_adv_map::t_tile const & tile =
				impl.m_adventure_map.get_tile( t_level_map_point_2d( tile_point, impl.m_view_level ) );
			visibility = tile.get_visibility( impl.m_team_view );
		}
		else
		{
			// Not pointing at a tile so check the shroud mask
			t_screen_point mask_point = point + impl.m_buffer_origin;
			t_screen_point mask_size( impl.m_shroud_mask_ptr->get_width(), impl.m_shroud_mask_ptr->get_height() );
			if ( mask_point.x >= mask_size.x )
				mask_point.x -= mask_size.x;
			if ( mask_point.y >= mask_size.y )
				mask_point.y -= mask_size.y;

			// If shroud is completely opaque at this pixel then consider it unexplored
			if ( *( impl.m_shroud_mask_ptr->advance_line( impl.m_shroud_mask_ptr->get_data_ptr(), mask_point.y ) + mask_point.x ) >= 15 )
				visibility = k_tile_never_explored;
		}
	}

	// If the point is on a tile under the shroud, then return nothing
	if ( visibility == k_tile_never_explored )
		return t_adv_map::k_invalid_object_id;

	// Get the view cell at this point
	t_screen_point view_cell_point = ( impl.m_view_pos + point ) >> k_adventure_map_view_cell_shift;

	if ( !impl.m_adventure_map.is_valid_view_cell( impl.m_view_level, view_cell_point ) )
		return t_adv_map::k_invalid_object_id;

	t_view_cell const & view_cell
		= impl.m_adventure_map.get_view_cell( impl.m_view_level, view_cell_point );

	// Check the object subimages
	t_view_cell::t_const_object_subimage_info_reverse_iterator subimage_info_iter = view_cell.object_subimage_rbegin();
	for ( ; subimage_info_iter != view_cell.object_subimage_rend(); ++subimage_info_iter )
	{
		t_object_id object_id = subimage_info_iter->object_id;
		if ( exclude_func( object_id ) )
			continue;

		t_object const & object = impl.m_adventure_map.get_object( object_id );
		if ( !object.is_visible_to( impl.m_team_view ) )
			continue;

		t_level_map_point_2d object_pos = impl.m_adventure_map.get_object_pos( object_id );
		t_screen_point obj_screen_point = impl.m_adventure_map.get_screen_point( object_pos ) - impl.m_view_pos;
		obj_screen_point.y -= impl.m_adventure_map.get_object_height( object_id );
		if ( object.hit_test( impl.get_last_update_time( object_id, object ), point - obj_screen_point ) )
			return object_id;
	}

	return t_adv_map::k_invalid_object_id;
}

/////////////////////////////////////////////////////////////////////////////
t_screen_point t_map_renderer::clamp_to_map( t_screen_point const & point ) const
{
	t_impl & impl = *m_impl_ptr;

	t_screen_point result = point + impl.m_view_pos;

	t_screen_point map_view_size = impl.m_adventure_map.get_view_size();

	if ( result.x < 0 )
		result.x = 0;
	else if ( result.x >= map_view_size.x )
		result.x = map_view_size.x - 1;

	t_internal_map_data const & internal_map_data = impl.get_internal_map_data();
	int margin_y = -internal_map_data.get_top_margin_height( impl.m_view_level, result.x );
	if ( result.y < margin_y )
		result.y = margin_y;
	else
	{
		margin_y = map_view_size.y - internal_map_data.get_bottom_margin_height( impl.m_view_level, result.x );
		if ( result.y >= margin_y )
			result.y = margin_y - 1;
	}

	return result - impl.m_view_pos;
}

/////////////////////////////////////////////////////////////////////////////
int t_map_renderer::get_team_view() const
{
	t_impl const & impl = *m_impl_ptr;
	return impl.m_team_view;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::set_team_view( int team_view )
{
	t_impl & impl = *m_impl_ptr;

	if ( impl.m_team_view == team_view )
		return;

	if ( impl.m_view_size.x > 0 && impl.m_view_size.y > 0 )
	{
		impl.clear_animating_object_ids();

		std::swap( impl.m_team_view, team_view );

		t_screen_rect view_rect( t_screen_point( 0, 0 ), impl.m_view_size );

		// Create a new shroud mask buffer
		if ( impl.m_team_view >= 0 )
		{
			if ( team_view < 0 )
				impl.m_shroud_mask_ptr.reset( new t_impl::t_shroud_mask( impl.m_view_size.x, impl.m_view_size.y ) );
			impl.update_shroud_mask( view_rect );
		}
		else if ( team_view >= 0 )
			impl.m_shroud_mask_ptr.reset();

		impl.record_animating_object_ids_in_rect( view_rect );

		//t_clip_list( view_rect ).swap( impl.m_dirty_list );
		impl.m_dirty_quadtree.clear( view_rect );

		// The entire back buffer has been dirtied
		std::vector< t_screen_rect > dirtied_rects( 1, view_rect );
		on_rects_dirtied( dirtied_rects );
	}
	else
		impl.m_team_view = team_view;
}

/////////////////////////////////////////////////////////////////////////////
t_screen_rect t_map_renderer::get_adv_object_extent( t_object_id object_id ) const
{
	t_impl const & impl = *m_impl_ptr;
	t_object const & object = impl.m_adventure_map.get_object( object_id );
	return object.get_rect() + impl.get_adv_object_screen_point( object_id );
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_adv_map const & t_map_renderer::get_adventure_map() const
{
	return m_impl_ptr->m_adventure_map;
}

/////////////////////////////////////////////////////////////////////////////
bool t_map_renderer::get_view_grid() const
{
	return m_impl_ptr->m_view_grid;
}

/////////////////////////////////////////////////////////////////////////////
int t_map_renderer::get_view_level() const
{
	return m_impl_ptr->m_view_level;
}

/////////////////////////////////////////////////////////////////////////////
bool t_map_renderer::get_view_passability() const
{
	return m_impl_ptr->m_view_passability;
}

/////////////////////////////////////////////////////////////////////////////
t_screen_point t_map_renderer::get_view_pos() const
{
	return m_impl_ptr->m_view_pos;
}

/////////////////////////////////////////////////////////////////////////////
t_screen_point t_map_renderer::get_view_size() const
{
	return m_impl_ptr->m_view_size;
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::refresh( t_adv_map const & adventure_map )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->refresh();
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_adv_object_moved(
	t_adv_map const &	adventure_map,
	t_object_id			object_id )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_adv_object_moved( object_id );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_adv_object_moved(
	t_adv_map const &					adventure_map,
	t_object_id							object_id,
	int									level,
	t_tile_point_set const &			changed_tile_points,
	t_object_height_delta_set const &	changed_object_height_deltas )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
	{
		internal_map_data_ptr->on_adv_object_moved(
			object_id,
			level,
			changed_tile_points,
			changed_object_height_deltas );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_adv_object_placed(
	t_adv_map const &	adventure_map,
	t_object_id			object_id )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_adv_object_placed( object_id );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_adv_object_placed(
	t_adv_map const &					adventure_map,
	t_object_id							object_id,
	int									level,
	t_tile_point_set const &			changed_tile_points,
	t_object_height_delta_set const &	changed_object_height_deltas )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
	{
		internal_map_data_ptr->on_adv_object_placed(
			object_id,
			level,
			changed_tile_points,
			changed_object_height_deltas );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_adv_object_removed(
	t_adv_map const &				adventure_map,
	t_object const &				object,
	t_level_map_point_2d const &	object_pos )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_adv_object_removed( object, object_pos );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_moving_adv_object(
	t_adv_map const &			adventure_map,
	t_object_id					object_id )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_moving_adv_object( object_id );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_multiple_adv_objects_removed(
	t_adv_map const & adventure_map )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_multiple_adv_objects_removed();
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_removing_adv_object(
	t_adv_map const &	adventure_map,
	t_object_id			object_id )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_removing_adv_object( object_id );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_removing_multiple_adv_objects(
	t_adv_map const &		adventure_map,
	t_object_id_set const &	object_ids )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_removing_multiple_adv_objects( object_ids );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_rock_terrain_changed(
	t_adv_map const &			adventure_map,
	int							level,
	t_tile_point_set const &	changed_tile_points,
	t_object_id_set const &		placed_object_ids )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
	{
		internal_map_data_ptr->on_rock_terrain_changed(
			level,
			changed_tile_points,
			placed_object_ids );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_terrain_changed(
	t_adv_map const &			adventure_map,
	int							level,
	t_tile_point_set const &	changed_tile_points )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_terrain_changed( level, changed_tile_points );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_terrain_changed(
	t_adv_map const &					adventure_map,
	int									level,
	t_tile_point_set const &			changed_tile_points,
	t_object_height_delta_set const &	changed_object_height_deltas )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
	{
		internal_map_data_ptr->on_terrain_changed(
			level,
			changed_tile_points,
			changed_object_height_deltas );
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_visibility_changed(
	t_adv_map const &			adventure_map,
	int							team_num,
	int							level,
	t_visibility_set const &	changed_visibility_set,
	t_tile_point_set const &	changed_shroud_set )
{
	t_internal_map_data * internal_map_data_ptr;
	
	internal_map_data_ptr = get_internal_map_data_ptr( adventure_map );
	if ( internal_map_data_ptr != 0 )
		internal_map_data_ptr->on_visibility_changed( team_num, level, changed_visibility_set, changed_shroud_set );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::move_view( t_screen_point const & new_view_pos )
{
	t_impl & impl = *m_impl_ptr;

	if ( new_view_pos == impl.m_view_pos )
		return;

	// Get the scroll delta
	t_screen_point scroll_delta = new_view_pos - impl.m_view_pos;

	// Set the view position
	impl.m_view_pos = new_view_pos;
	
	// Notify our client of the move
	on_view_moved( impl.m_view_level, impl.m_view_pos, impl.m_view_size );

	if ( impl.m_view_size.x == 0 || impl.m_view_size.y == 0 )
		return;

	// If the scroll is larger than the view size simply redraw the entire view
	if (	scroll_delta.x >= impl.m_view_size.x
		||	scroll_delta.x <= -impl.m_view_size.x
		||	scroll_delta.y >= impl.m_view_size.y
		||	scroll_delta.y <= -impl.m_view_size.y )
	{
		impl.m_buffer_origin = t_screen_point( 0, 0 );
		t_screen_rect rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y );
		impl.update_terrain_buffer( rect );
		impl.update_shroud_mask( rect );

		//t_clip_list( rect ).swap( impl.m_dirty_list );
		impl.m_dirty_quadtree.clear( rect );

		impl.clear_animating_object_ids();
		impl.record_animating_object_ids_in_rect( rect );
	}
	else
	{
		t_screen_rect view_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y );

		// Scroll the back buffer and redraw the uncovered edges
		scroll( *impl.m_back_buffer_ptr, view_rect, -scroll_delta );				//major performance penalty ~1500

		//impl.m_dirty_list.offset( -scroll_delta );
		//impl.m_dirty_list.intersect( view_rect );

		impl.m_dirty_quadtree.offset( -scroll_delta );
		impl.m_dirty_quadtree.intersect( view_rect );

		impl.reevaluate_animating_object_ids();

		impl.m_buffer_origin += scroll_delta;
		
		if ( impl.m_buffer_origin.x < 0 )
			impl.m_buffer_origin.x += impl.m_view_size.x;
		else if ( impl.m_buffer_origin.x >= impl.m_view_size.x )
			impl.m_buffer_origin.x -= impl.m_view_size.x;

		if ( impl.m_buffer_origin.y < 0 )
			impl.m_buffer_origin.y += impl.m_view_size.y;
		else if ( impl.m_buffer_origin.y >= impl.m_view_size.y )
			impl.m_buffer_origin.y -= impl.m_view_size.y;

		t_screen_rect horiz_uncovered_rect;
		horiz_uncovered_rect.top = 0;
		horiz_uncovered_rect.bottom = impl.m_view_size.y;

		t_screen_rect vert_uncovered_rect;
		vert_uncovered_rect.left = 0;
		vert_uncovered_rect.right = impl.m_view_size.x;

		if ( scroll_delta.x >= 0 )
		{
			horiz_uncovered_rect.left = impl.m_view_size.x - scroll_delta.x;
			horiz_uncovered_rect.right = impl.m_view_size.x;
			vert_uncovered_rect.right = horiz_uncovered_rect.left;
		}
		else
		{
			horiz_uncovered_rect.left = 0;
			horiz_uncovered_rect.right = -scroll_delta.x;
			vert_uncovered_rect.left = horiz_uncovered_rect.right;
		}

		if ( scroll_delta.y >= 0 )
		{
			vert_uncovered_rect.top = impl.m_view_size.y - scroll_delta.y;
			vert_uncovered_rect.bottom = impl.m_view_size.y;
		}
		else
		{
			vert_uncovered_rect.top = 0;
			vert_uncovered_rect.bottom = -scroll_delta.y;
		}

		if ( horiz_uncovered_rect.width() > 0 )
		{
			impl.update_terrain_buffer( horiz_uncovered_rect );
			impl.update_shroud_mask( horiz_uncovered_rect );

			//impl.m_dirty_list += horiz_uncovered_rect;
			impl.m_dirty_quadtree.insert( horiz_uncovered_rect , false );

			impl.record_animating_object_ids_in_rect( horiz_uncovered_rect );
		}

		if ( vert_uncovered_rect.height() > 0 )
		{
			impl.update_terrain_buffer( vert_uncovered_rect );
			impl.update_shroud_mask( vert_uncovered_rect );

			//impl.m_dirty_list += vert_uncovered_rect;
			impl.m_dirty_quadtree.insert( vert_uncovered_rect , false );

			impl.record_animating_object_ids_in_rect( vert_uncovered_rect );
		}
	}

	// The entire back buffer has been dirtied
	std::vector< t_screen_rect > dirtied_rects( 1, t_screen_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y ) );
	on_rects_dirtied( dirtied_rects );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::move_view(
	t_screen_point const &	new_view_pos,
	t_screen_point const &	new_view_size )
{
	t_impl & impl = *m_impl_ptr;

	t_screen_rect new_view_rect(new_view_pos, new_view_size);

	impl.m_dirty_quadtree.preallocate_quadtree_nodes(new_view_rect);

	if ( new_view_size == impl.m_view_size )
	{
		move_view( new_view_pos );
		return;
	}

	// Save the old view pos and view size
	t_screen_point old_view_pos = impl.m_view_pos;
	t_screen_point old_view_size = impl.m_view_size;

	// Set the new view pos and size
	impl.m_view_pos = new_view_pos;
	impl.m_view_size = new_view_size;

	// Notify our client of the move
	on_view_moved( impl.m_view_level, impl.m_view_pos, impl.m_view_size );

	if ( new_view_size.x > 0 && new_view_size.y > 0 )
	{
		// Save the old back buffer
		t_back_buffer_ptr old_back_buffer_ptr = impl.m_back_buffer_ptr;

		// Create a new terrain buffer
		t_impl::t_terrain_buffer_ptr terrain_buffer_ptr( new t_memory_bitmap16( new_view_size.x, new_view_size.y ) );

		// Create a new shroud mask buffer
		t_impl::t_shroud_mask_ptr shroud_mask_ptr;
		if( impl.m_team_view >= 0 )
			shroud_mask_ptr.reset( new t_impl::t_shroud_mask( new_view_size.x, new_view_size.y ) );

		// Create a new back buffer
		impl.m_back_buffer_ptr = create_back_buffer( new_view_size );
		assert( impl.m_back_buffer_ptr.get() != 0 );
		assert( impl.m_back_buffer_ptr->get_width() >= new_view_size.x );
		assert( impl.m_back_buffer_ptr->get_height() >= new_view_size.y );

		terrain_buffer_ptr.swap( impl.m_terrain_buffer_ptr );
		shroud_mask_ptr.swap( impl.m_shroud_mask_ptr );

		if ( old_back_buffer_ptr.get() != 0 )
		{
			assert( terrain_buffer_ptr.get() != 0 );

			t_screen_rect new_view_rect( new_view_pos, new_view_size );
			t_screen_rect old_view_rect( old_view_pos, old_view_size );

			if ( intersect( new_view_rect, old_view_rect ) )
			{
				t_screen_rect blt_rect = intersection( new_view_rect, old_view_rect );

				// Blt the old back buffer onto the new one
				bit_blt(
					*impl.m_back_buffer_ptr,
					blt_rect.top_left() - new_view_pos,
					*old_back_buffer_ptr,
					blt_rect.top_left() - old_view_pos,
					blt_rect.size() );

				//impl.m_dirty_list.intersect( blt_rect - old_view_pos );
				//impl.m_dirty_list.offset( old_view_pos - new_view_pos );

				impl.m_dirty_quadtree.intersect( blt_rect - old_view_pos );
				impl.m_dirty_quadtree.offset( old_view_pos - new_view_pos );


				// Blt the old terrain buffer onto the new one
				t_screen_rect quadrant_rect;
				quadrant_rect.left = old_view_rect.left;
				quadrant_rect.top = old_view_rect.top;
				quadrant_rect.right = old_view_rect.right - impl.m_buffer_origin.x;
				quadrant_rect.bottom = old_view_rect.bottom - impl.m_buffer_origin.y;
				if ( intersect( new_view_rect, quadrant_rect ) )
				{
					blt_rect = intersection( new_view_rect, quadrant_rect );

					t_screen_point	dest_point	= blt_rect.top_left() - new_view_pos;
					t_screen_point	src_point	= blt_rect.top_left() - old_view_pos + impl.m_buffer_origin;
					t_screen_point	size		= blt_rect.size();

					bit_blt(
						*impl.m_terrain_buffer_ptr,
						dest_point,
						*terrain_buffer_ptr,
						src_point,
						size );

					if( impl.m_team_view >= 0 )
						bit_blt(
							*impl.m_shroud_mask_ptr,
							dest_point,
							*shroud_mask_ptr,
							src_point,
							size );
				}

				quadrant_rect.left = quadrant_rect.right;
				quadrant_rect.right = old_view_rect.right;
				if ( intersect( new_view_rect, quadrant_rect ) )
				{
					blt_rect = intersection( new_view_rect, quadrant_rect );

					t_screen_point	dest_point	= blt_rect.top_left() - new_view_pos;
					t_screen_point	src_point	= blt_rect.top_left() - old_view_pos + impl.m_buffer_origin - t_screen_point( old_view_size.x, 0 );
					t_screen_point	size		= blt_rect.size();

					bit_blt(
						*impl.m_terrain_buffer_ptr,
						dest_point,
						*terrain_buffer_ptr,
						src_point,
						size );

					if( impl.m_team_view >= 0 )
						bit_blt(
							*impl.m_shroud_mask_ptr,
							dest_point,
							*shroud_mask_ptr,
							src_point,
							size );
				}

				quadrant_rect.left = old_view_rect.left;
				quadrant_rect.right = old_view_rect.right - impl.m_buffer_origin.x;
				quadrant_rect.top = quadrant_rect.bottom;
				quadrant_rect.bottom = old_view_rect.bottom;
				if ( intersect( new_view_rect, quadrant_rect ) )
				{
					blt_rect = intersection( new_view_rect, quadrant_rect );

					t_screen_point	dest_point	= blt_rect.top_left() - new_view_pos;
					t_screen_point	src_point	= blt_rect.top_left() - old_view_pos + impl.m_buffer_origin - t_screen_point( 0, old_view_size.y );
					t_screen_point	size		= blt_rect.size();

					bit_blt(
						*impl.m_terrain_buffer_ptr,
						dest_point,
						*terrain_buffer_ptr,
						src_point,
						size );

					if( impl.m_team_view >= 0 )
						bit_blt(
							*impl.m_shroud_mask_ptr,
							dest_point,
							*shroud_mask_ptr,
							src_point,
							size );

				}

				quadrant_rect.left = quadrant_rect.right;
				quadrant_rect.right = old_view_rect.right;
				if ( intersect( new_view_rect, quadrant_rect ) )
				{
					blt_rect = intersection( new_view_rect, quadrant_rect );

					t_screen_point	dest_point	= blt_rect.top_left() - new_view_pos;
					t_screen_point	src_point	= blt_rect.top_left() - old_view_pos + impl.m_buffer_origin - old_view_size;
					t_screen_point	size		= blt_rect.size();

					bit_blt(
						*impl.m_terrain_buffer_ptr,
						dest_point,
						*terrain_buffer_ptr,
						src_point,
						size );

					if( impl.m_team_view >= 0 )
						bit_blt(
							*impl.m_shroud_mask_ptr,
							dest_point,
							*shroud_mask_ptr,
							src_point,
							size );
				}
			}
		}
		else
			assert( terrain_buffer_ptr.get() == 0 );

		// Kill the old back buffer
		old_back_buffer_ptr = 0;
		terrain_buffer_ptr.reset();
		shroud_mask_ptr.reset();

		impl.m_buffer_origin = t_screen_point( 0, 0 );

		impl.reevaluate_animating_object_ids();

		// Draw any newly uncovered areas
		t_screen_rect horiz_uncovered_rect;
		horiz_uncovered_rect.top = 0;
		horiz_uncovered_rect.bottom = new_view_size.y;

		t_screen_rect vert_uncovered_rect;
		vert_uncovered_rect.left = 0;
		vert_uncovered_rect.right = new_view_size.x;

		if ( old_view_pos.x > new_view_pos.x )
		{
			horiz_uncovered_rect.left = 0;
			horiz_uncovered_rect.right = old_view_pos.x - new_view_pos.x;
			if ( horiz_uncovered_rect.right > new_view_size.x )
				horiz_uncovered_rect.right = new_view_size.x;
			vert_uncovered_rect.left = horiz_uncovered_rect.right;

			impl.update_terrain_buffer( horiz_uncovered_rect );
			impl.update_shroud_mask( horiz_uncovered_rect );
			
			//impl.m_dirty_list += horiz_uncovered_rect;
			impl.m_dirty_quadtree.insert( horiz_uncovered_rect, false );

			impl.record_animating_object_ids_in_rect( horiz_uncovered_rect );
		}

		if ( new_view_pos.x + new_view_size.x > old_view_pos.x + old_view_size.x )
		{
			horiz_uncovered_rect.left = new_view_size.x - ( ( new_view_pos.x + new_view_size.x ) - ( old_view_pos.x + old_view_size.x ) );
			if ( horiz_uncovered_rect.left < 0 )
				horiz_uncovered_rect.left = 0;
			horiz_uncovered_rect.right = new_view_size.x;
			vert_uncovered_rect.right = horiz_uncovered_rect.left;

			impl.update_terrain_buffer( horiz_uncovered_rect );
			impl.update_shroud_mask( horiz_uncovered_rect );

			//impl.m_dirty_list += horiz_uncovered_rect;
			impl.m_dirty_quadtree.insert( horiz_uncovered_rect, false );

			impl.record_animating_object_ids_in_rect( horiz_uncovered_rect );
		}

		if ( old_view_pos.y > new_view_pos.y )
		{
			vert_uncovered_rect.top = 0;
			vert_uncovered_rect.bottom = old_view_pos.y - new_view_pos.y;
			if ( vert_uncovered_rect.bottom > new_view_size.y )
				vert_uncovered_rect.bottom = new_view_size.y;

			impl.update_terrain_buffer( vert_uncovered_rect );
			impl.update_shroud_mask( vert_uncovered_rect );

			//impl.m_dirty_list += vert_uncovered_rect;
			impl.m_dirty_quadtree.insert( vert_uncovered_rect, false );

			impl.record_animating_object_ids_in_rect( vert_uncovered_rect );
		}

		if ( new_view_pos.y + new_view_size.y > old_view_pos.y + old_view_size.y )
		{
			vert_uncovered_rect.top = new_view_size.y - ( ( new_view_pos.y + new_view_size.y ) - ( old_view_pos.y + old_view_size.y ) );
			if ( vert_uncovered_rect.top < 0 )
				vert_uncovered_rect.top = 0;
			vert_uncovered_rect.bottom = new_view_size.y;

			impl.update_terrain_buffer( vert_uncovered_rect );
			impl.update_shroud_mask( vert_uncovered_rect );
			
			//impl.m_dirty_list += vert_uncovered_rect;
			impl.m_dirty_quadtree.insert( vert_uncovered_rect, false );

			impl.record_animating_object_ids_in_rect( vert_uncovered_rect );
		}

		// The entire back buffer has been dirtied
		std::vector< t_screen_rect > dirtied_rects( 1, t_screen_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y ) );
		on_rects_dirtied( dirtied_rects );
	}
	else
	{
		impl.m_back_buffer_ptr = 0;
		impl.m_terrain_buffer_ptr.reset();
		impl.m_shroud_mask_ptr.reset();
		impl.clear_animating_object_ids();
	}
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::refresh()
{
	t_impl & impl = *m_impl_ptr;

	if ( impl.m_view_size.x <= 0 || impl.m_view_size.y <= 0 )
		return;

	t_screen_rect rect( t_screen_point( 0, 0 ), impl.m_view_size );

	// Redraw the entire view
	impl.update_terrain_buffer( rect );
	impl.update_shroud_mask( rect );

	//impl.m_dirty_list += rect;
	impl.m_dirty_quadtree.insert( rect, false );

	impl.clear_animating_object_ids();
	impl.record_animating_object_ids_in_rect( t_screen_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y ) );

	// The entire back buffer has been dirtied
	std::vector< t_screen_rect > dirtied_rects( 1, rect );
	on_rects_dirtied( dirtied_rects );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::set_view_grid( bool new_view_grid )
{
	t_impl & impl = *m_impl_ptr;

	if ( new_view_grid == impl.m_view_grid )
		return;

	// Set the view grid
	impl.m_view_grid = new_view_grid;

	refresh();
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::set_view_level( int new_view_level )
{
	t_impl & impl = *m_impl_ptr;

	assert( new_view_level >= 0 && new_view_level < impl.m_adventure_map.get_num_levels() );

	if ( new_view_level == impl.m_view_level )
		return;

	// Set the view level
	impl.m_view_level = new_view_level;

	impl.clear_animating_object_ids();
	impl.record_animating_object_ids_in_rect( t_screen_rect( 0, 0, impl.m_view_size.x, impl.m_view_size.y ) );

	// Notify our client of the move
	on_view_moved( impl.m_view_level, impl.m_view_pos, impl.m_view_size );

	refresh();
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::set_view_passability( bool new_view_passability )
{
	t_impl & impl = *m_impl_ptr;

	if ( new_view_passability == impl.m_view_passability )
		return;

	// Set view passability
	impl.m_view_passability = new_view_passability;

	refresh();
}

/////////////////////////////////////////////////////////////////////////////
bool t_map_renderer::subtile_hit_test( t_screen_point const & point, t_map_point_2d & result ) const
{
	t_impl & impl = *m_impl_ptr;

	t_map_point_2d tile_point;
	if ( !tile_hit_test( point, tile_point ) )
		return false;

	t_adv_map::t_const_tile_vertex_quad tile_vertex_quad =
		impl.m_adventure_map.get_tile_vertex_quad( t_level_map_point_2d( tile_point, impl.m_view_level ) );

	t_screen_point relative_point = point + impl.m_view_pos - impl.m_adventure_map.get_screen_point( tile_point );

	int top_y = -tile_vertex_quad.top.get_height();
	int bottom_y = k_adventure_tile_height - tile_vertex_quad.bottom.get_height();

	int relative_x;
	int side_height;
	if ( relative_point.x >= 0 )
	{
		relative_x = relative_point.x;
		side_height = tile_vertex_quad.right.get_height();
	}
	else
	{
		relative_x = -relative_point.x - 1;
		side_height = tile_vertex_quad.left.get_height();
	}

	int side_y = k_adventure_tile_height / 2 - side_height;

	int start_y = relative_x * ( k_adventure_tile_height / 2 ) / ( k_adventure_tile_width / 2 );
	int end_y = k_adventure_tile_height - start_y - 1;

	int adjusted_start_y = top_y + ( side_y - top_y ) * relative_x / ( k_adventure_tile_width / 2 );
	int adjusted_end_y = side_y + ( bottom_y - side_y ) * ( k_adventure_tile_width / 2 - relative_x - 1 ) / ( k_adventure_tile_width / 2 );
	assert( relative_point.y >= adjusted_start_y && relative_point.y < adjusted_end_y );

	relative_point.y = start_y + ( ( relative_point.y - adjusted_start_y ) * ( end_y - start_y ) / ( adjusted_end_y - adjusted_start_y ) );
	relative_point.x += k_adventure_tile_width / 2;

	t_map_point_2d subtile_point = convert_screen_point_to_map( relative_point, k_adventure_subtile_height, k_adventure_tile_screen_size, k_adventure_subtiles_per_tile );
	assert( subtile_point.row >= 0 && subtile_point.row < k_adventure_subtiles_per_tile );
	assert( subtile_point.column >= 0 && subtile_point.column < k_adventure_subtiles_per_tile );

	result = ( tile_point << k_adventure_subtile_shift ) | subtile_point;
	return true;
}

/////////////////////////////////////////////////////////////////////////////
bool t_map_renderer::tile_hit_test( t_screen_point const & point, t_map_point_2d & result ) const
{
	t_impl & impl = *m_impl_ptr;

	t_screen_point map_view_point = point + impl.m_view_pos;
	if ( map_view_point.x < 0 || map_view_point.y < -k_max_tile_vertex_height )
		return false;

	t_screen_point map_view_size = impl.m_adventure_map.get_view_size();
	if ( map_view_point.x >= map_view_size.x || map_view_point.y >= map_view_size.y )
		return false;

	if ( map_view_point.y < 0 )
	{
		int top_margin_y = -impl.get_internal_map_data().get_top_margin_height( impl.m_view_level, map_view_point.x );
		if ( map_view_point.y < top_margin_y )
			return false;
	}
	
	if ( map_view_point.y >= map_view_size.y - k_max_tile_vertex_height )
	{
		int bottom_margin_y = map_view_size.y - impl.get_internal_map_data().get_bottom_margin_height( impl.m_view_level, map_view_point.x );
		if ( map_view_point.y >= bottom_margin_y )
			return false;
	}

	t_map_point_2d tile_map_point =
		impl.m_adventure_map.get_map_point( t_screen_point( map_view_point.x, map_view_point.y >= 0 ? map_view_point.y : 0 ) );
	t_screen_point tile_screen_point =
		impl.m_adventure_map.get_screen_point( tile_map_point ) - impl.m_view_pos;

	int relative_x;
	t_map_point_2d side_tile_point = tile_map_point;
	if ( point.x >= tile_screen_point.x )
	{
		relative_x = tile_screen_point.x + k_adventure_tile_width / 2 - point.x - 1;
		++side_tile_point.column;
	}
	else
	{
		relative_x = point.x - tile_screen_point.x + k_adventure_tile_width / 2;
		++side_tile_point.row;
	}

	int tile_side_y =
		tile_screen_point.y + k_adventure_tile_height / 2 - impl.m_adventure_map.get_tile_vertex( t_level_map_point_2d( side_tile_point, impl.m_view_level ) ).get_height();

	while ( true )
	{
		int tile_bottom_y =
			tile_screen_point.y + k_adventure_tile_height - impl.m_adventure_map.get_tile_vertex( t_level_map_point_2d( tile_map_point + t_map_point_2d( 1, 1 ), impl.m_view_level ) ).get_height();
		int tile_end_y =
			tile_side_y + ( tile_bottom_y - tile_side_y ) * relative_x / ( k_adventure_tile_width / 2 );

		if ( tile_end_y > point.y )
			break;

		relative_x = ( k_adventure_tile_width / 2 ) - relative_x - 1;
		tile_side_y = tile_bottom_y;
		if ( point.x >= tile_screen_point.x )
		{
			++tile_map_point.column;
			tile_screen_point.x += k_adventure_tile_width / 2;
		}
		else
		{
			++tile_map_point.row;
			tile_screen_point.x -= k_adventure_tile_width / 2;
		}
		tile_screen_point.y += k_adventure_tile_height / 2;
	}

	result = tile_map_point;
	return true;
}

/////////////////////////////////////////////////////////////////////////////
t_map_renderer::t_back_buffer_ptr t_map_renderer::create_back_buffer(
	t_screen_point const & view_size )
{
	t_impl & impl = *m_impl_ptr;

	assert( impl.m_client_ptr != 0 );
	return impl.m_client_ptr->create_back_buffer( view_size );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_rects_dirtied(
	std::vector< t_screen_rect > const & dirtied_rects )
{
	t_impl & impl = *m_impl_ptr;

	assert( impl.m_client_ptr != 0 );
	impl.m_client_ptr->on_rects_dirtied( dirtied_rects );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::on_view_moved(
	int						new_view_level,
	t_screen_point const &	new_view_pos,
	t_screen_point const &	new_view_size )
{
	t_impl & impl = *m_impl_ptr;

	assert( impl.m_client_ptr != 0 );
	impl.m_client_ptr->on_view_moved( new_view_level, new_view_pos, new_view_size );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::enable_animation( bool arg )
{
	m_impl_ptr->enable_animation( arg );
}

/////////////////////////////////////////////////////////////////////////////
void t_map_renderer::update( t_screen_rect const & rect )
{
	t_impl & impl = *m_impl_ptr;

	t_sorted_clip_list::t_rect_list				intersect_list;

	// const & clip_list = intersection( impl.m_dirty_list, rect );
	//intersection_cached( impl.m_dirty_list, impl.m_intersect_list, rect );

	impl.m_dirty_quadtree.intersect( rect , intersect_list);

	t_sorted_clip_list::t_rect_list::const_iterator			rect_end  = intersect_list.end();
	t_sorted_clip_list::t_rect_list::const_iterator			rect_iter = intersect_list.begin();

	for ( ; rect_iter != rect_end; ++rect_iter )
		impl.draw_map( *rect_iter );

	//impl.m_dirty_list -= rect;
	impl.m_dirty_quadtree.remove( rect );
}
